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

2021-01-28 09:13

阅读:475

标签:内容   jwt   下载失败   下载方式   alt   下载   match   用户   ade   

原文:如何在启用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中的二进制容器, 它可以存储文件的二进制流。所以我们可以通过如下思路完成文件下载:

  1. 创建一个异步请求来下载文件的二进制流,这个请求的头部需要附加Bearer Token,在方法回调中,我们将文件二进制流保存在一个Blob对象中
  2. 我们使用Javascript添加一个虚拟的超链接,超链接的href属性指向了刚刚的Blob对象。
  3. 我们通过模拟点击这个虚拟的超链接,来完成文件下载的功能。
Copy
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); });

这个方案有两个缺点:

  1. 就是只有当文件流完全读取到Blob对象中之后,才会触发真正的文件下载。因此如果文件内容过大话,浏览器会有一个长时间的静止,当文件流全部加载到Blob对象之后,才会触发下载操作。所以这里可能需要自己添加一个Loading效果,给用户一些提示。
  2. 并不是所有的浏览器都支持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的实例代码:

Copy
[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的实例代码:

Copy
[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项目中下载文件

标签:内容   jwt   下载失败   下载方式   alt   下载   match   用户   ade   

原文地址:https://www.cnblogs.com/lonelyxmas/p/11904941.html


评论


亲,登录后才可以留言!