如何在启用JWT Token授权的.NET Core WebApi项目中下载文件

背景
前几天,做项目的时候遇到一个文件下载的问题。当前系统是一个前后端分离的项目,前端是一个AngularJs项目, 后端是一个.NET Core WebApi项目。后端的Api项目使用了Jwt Token授权,所以每个Api请求都需要传递一个Bearer Token。
这一切都看起来理所当然,但是当需要从WebApi下载文件的时候,出现了问题。以前下载文件的时候,我们可以在Javascript中使用window.open('[文件下载Api]')的方式下载文件,但是这个方法不能接收Bearer Token, 所以就会导致文件下载失败,返回一个401未授权的响应码。
可能有的同学会将这个文件下载Api设置成允许匿名访问,但是这样会导致系统不安全。
那么有什么好一点的方式可以解决这个问题呢?
解决方案
使用Blob对象
Blob对象可以看做是Javascript中的二进制容器, 它可以存储文件的二进制流。所以我们可以通过如下思路完成文件下载:
- 创建一个异步请求来下载文件的二进制流,这个请求的头部需要附加Bearer Token,在方法回调中,我们将文件二进制流保存在一个Blob对象中
- 我们使用Javascript添加一个虚拟的超链接,超链接的href属性指向了刚刚的Blob对象。
- 我们通过模拟点击这个虚拟的超链接,来完成文件下载的功能。
let anchor = document.createElement("a");
let file = 'https://www.example.com/api/getFiles/'+fileId;
let headers = new Headers();
headers.append('Authorization', 'Bearer MY-TOKEN');
fetch(file, { headers })
.then(response => response.blob())
.then(blobby => {
let objectUrl = window.URL.createObjectURL(blobby);
anchor.href = objectUrl;
anchor.download = 'some-file.pdf';
anchor.click();
window.URL.revokeObjectURL(objectUrl);
});
这个方案有两个缺点:
- 就是只有当文件流完全读取到Blob对象中之后,才会触发真正的文件下载。因此如果文件内容过大话,浏览器会有一个长时间的静止,当文件流全部加载到Blob对象之后,才会触发下载操作。所以这里可能需要自己添加一个Loading效果,给用户一些提示。
- 并不是所有的浏览器都支持Blob对象,在一些老的浏览器中Blob对象是不被支持的。
使用ASP.NET Core中的Data Protection
在之前的博客中,我有讲解过ASP.NET Core中的Data Protection功能, 我们可以使用Data Protection将一些敏感信息加密。所以这里我们可以将一个需要授权才能使用下载文件的Api, 替换成2个Api
第一个Api是需要授权的,它主要负责查看文件ID是否存在,如果存在,就使用Data Protection, 将这个ID加密,并返回给前端,这个ID的加密时效设置为5秒。
第二个Api是不需要授权的,允许匿名访问。它接收前一个Api提供的加密ID, 如果ID可以解密成功,就返回这个ID对应的文件流。
第一个Api的实例代码:
[HttpGet]
[Route("~/api/file_links/{fileId}")]
public IActionResult GetFileLink(Guid fileId)
{
if (_files.Any(p => p.FileId == fileId))
{
var matchedFile = _files.First(p => p.FileId == fileId);
return Content(this.protector.Protect(matchedFile.FileId.ToString(),
TimeSpan.FromSeconds(5)));
}
return StatusCode(500);
}
第二个Api的实例代码:
[HttpGet]
[AllowAnonymous]
[Route("~/api/raw_files/{id}")]
public IActionResult GetRawFile(string id)
{
try
{
var rawId = Guid.Parse(this.protector.Unprotect(id));
var matchedFile = _files.First(p => p.FileId == rawId);
matchedFile.FileContent.Position = 0;
return File(matchedFile.FileContent, "text/plain", "helloWorld.txt");
}
catch
{
return StatusCode(401);
}
}
使用这种方式,虽然我们开放了一个未经授权就可以访问的Api入口,但是由于使用了Data Protection, 所以对于非法的请求,系统也可以进行一定的屏蔽。
最终效果
针对以上2种下载方式,我创建了一个小项目,项目地址:https://github.com/lamondlu/Sample_DownloadFileInAuth, 打开之后页面如下。

普通下载
由于缺少Token, 所以下载失败,返回401

使用Blob下载
使用Blob下载之后,文件下载成功

使用Data Protection
使用Data Protection后,文件下载成功

总结
本文只算抛砖引玉,如果大家有更好的解决方案,欢迎一起讨论。
如何在启用JWT Token授权的.NET Core WebApi项目中下载文件的更多相关文章
- ASP.NET Core 实战:基于 Jwt Token 的权限控制全揭露
一.前言 在涉及到后端项目的开发中,如何实现对于用户权限的管控是需要我们首先考虑的,在实际开发过程中,我们可能会运用一些已经成熟的解决方案帮助我们实现这一功能,而在 Grapefruit.VuCore ...
- ASP.NET Core 3.1使用JWT认证Token授权 以及刷新Token
传统Session所暴露的问题 Session: 用户每次在计算机身份认证之后,在服务器内存中会存放一个session,在客户端会保存一个cookie,以便在下次用户请求时进行身份核验.但是这样就暴露 ...
- 温故知新,.Net Core遇见JWT(JSON Web Token)授权机制方案
什么是JWT JWT (JSON Web Token) 是一个开放标准,它定义了一种以紧凑和自包含的方法,用于在双方之间安全地传输编码为JSON对象的信息. 因此,简单来说,它是JSON格式的加密字符 ...
- DDD实战11 在项目中使用JWT的token 进行授权验证
步骤: 1.首先要在webapi的管道中 使用认证(Authentication) 2.要在webapi的服务中注册验证条件 代码如下: namespace Dealer.WebApi { publi ...
- 三分钟学会.NET Core Jwt 策略授权认证
一.前言 大家好我又回来了,前几天讲过一个关于Jwt的身份验证最简单的案例,但是功能还是不够强大,不适用于真正的项目,是的,在真正面对复杂而又苛刻的客户中,我们会不知所措,就现在需要将认证授权这一块也 ...
- ASP.NET Core搭建多层网站架构【10-使用JWT进行授权验证】
2020/01/31, ASP.NET Core 3.1, VS2019, Microsoft.AspNetCore.Authentication.JwtBearer 3.1.1 摘要:基于ASP.N ...
- Dotnet core使用JWT认证授权最佳实践(一)
最近,团队的小伙伴们在做项目时,需要用到JWT认证.遂根据自己的经验,整理成了这篇文章,用来帮助理清JWT认证的原理和代码编写操作. 一.JWT JSON Web Token (JWT)是一个开放标准 ...
- asp.net core 3.1 自定义中间件实现jwt token认证
asp.net core 3.1 自定义中间件实现jwt token认证 话不多讲,也不知道咋讲!直接上代码 认证信息承载对象[user] /// <summary> /// 认证用户信息 ...
- 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十四║ Vuex + JWT 实现授权验证登录
壹周回顾 哈喽,又是元气满满的一个周一,又与大家见面了,周末就是团圆节了,正好咱们的前后端也要团圆了,为什么这么说呢,因为以后的开发可能就需要前后端一起了,两边也终于会师了,还有几天Vue系列就基本告 ...
随机推荐
- Linux磁盘和文件系统管理
1.检测并确认新硬盘 挂载好新的硬盘设备并启动主机后,Linux系统会自动检测并加载该硬盘,无须额外安装驱动.执行“fdisk -l”命令即可查看,确认新增硬盘的设备名称和位置.作用:列出当前系统中所 ...
- Android- APP 秒开
Android- APP 秒开 1. 启动APP 时白屏或者黑屏 现象:当启动APP 时,会有一个白屏或者黑屏一闪而过,然后才会显示出主界面. 这是因为,我们新打开一个应用,系统会为这个应用创建一个进 ...
- Android常规布局方式和方法
一.关于给控件添加ID属性 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xm ...
- windows下编译SDL1.2
首先,官网下载开发库,我这里用的是tdm-gcc,因此下载mingw版的. 解压,写代码,编译…… 成功!...地出错了 这里提一下,编译命令是 g++ test.cpp -I include目录 ...
- linux磁盘满了的处理
1.查看磁盘使用情况 cd / df -h 如果 总量Size和Used一样,按就证明磁盘满了 2.查看当前文件下每个文件大小 du -sh * 一层一层去查,就可以查到占用空间最大的那个文件及产生 ...
- redis在windows和Linux系统下的下载、安装、配置
1.下载redis安装包 在redis的官网只有Linux系统下的安装包,微软的GitHub上有提供windows版本的redis安装包 redis中文网:http://www.redis.cn/ 微 ...
- c++ 读取不了hdf5文件中的字符串
问题描述: 在拿到一个hdf5文件,想用c++去读取文件中的字符串,但是会报错:read failed ps: c++读取hdf5的字符串方法见:https://support.hdfgroup.or ...
- 关于条件语句和 a && b
learn from javascript cookbook if (element == 'ab') array[index] = "**"; 该语句 等价于 if (eleme ...
- No Spring WebApplicationInitializer types detected on classpath 问题的一种解决办法
今天在idea中编译部署工程,tomcat报了这个错误: No Spring WebApplicationInitializer types detected on classpath 导致前端页面访 ...
- Kotlin基础
1.函数也是对象,可以作为参数和返回值 2.使用驼峰命名,尽量避免下划线 3.public函数应当有说明文档 4.lambda中花括号内前后都应该有空格 5.空值安全检查 var s: String ...