如何在启用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后,文件下载成功
总结
本文只算抛砖引玉,如果大家有更好的解决方案,欢迎一起讨论。
- 在ASP.NET Core Web API 项目里无法访问(wwwroot)下的文件
- ASP.NET Core WebAPI 开发-新建WebAPI项目 转
- .Net WebAPI 高速下载文件接口实现
- Java - 如何在基于Java的Web项目中实现文件上传和下载?
- asp.net core webapi文件上传
- ASP.NET Core 实战:使用ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目
- Asp.NetCoreWebApi图片上传接口(二)集成IdentityServer4授权访问(附源码)
- 如何测量并报告ASP.NET Core Web API请求的响应时间
- ASP.NET Core Web API 最小化项目
- SEO_ASP.net SEO优化(包含URL地址重写\viewState移动和压缩至服务器\SEO信息XML生成_根据URL地址重写文件)\web网站内容压缩 源码公开.本人授权可使用于商业项目。
- 用Middleware给ASP.NET Core Web API添加自己的授权验证
- asp.net core webapi 似乎未安装在 IIS 中承载 .NET Core 项目所需的 AspNetCoreModule。请尝试修复 Visual Studio 以纠正该问题。
- 一个简单的QQ隐藏图生成算法 通过jQuery和C#分别实现对.NET Core Web Api的访问以及文件上传
- asp.net core webapi项目配置全局路由
- 通过jQuery和C#分别实现对.NET Core Web Api的访问以及文件上传
- .NET Core WebApi中如何实现多态数据绑定实例代码
- ASP.NET Core Web API 最小化项目
- WEB项目中如何实现禁止下载文件(二)
- 在java的WEB项目如何防止浏览器直接打开下载的文件
- 如何在我自己的web 项目的jsp页面中添加链接,直接让别人通过内网在我的电脑上下载文件