如何在启用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系列就基本告 ...
随机推荐
- Anconda 3.7安装以及使用详细教程
Anconda 3.7安装以及使用详细教程 2019-04-17 22:42:03 一.下载anconda 3.7 链接地址:官方地址 二.安装 双击下载好的Anaconda3-2019.03- ...
- Vue面试中,经常会被问到的面试题/Vue知识点整理
一.对于MVVM的理解? MVVM 是 Model-View-ViewModel 的缩写.Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑.View 代表UI 组件,它负责将数 ...
- leetcode刷题四<寻找两个有序数组的中位数>
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n)). 你可以假设 nums1 和 nums2 ...
- yum的一些命令使用方法
yum 选项 参数 yum命令是在Fedora和RedHat以及SUSE中基于rpm的软件包管理器,它可以使系统管理人员交互和自动化地更细与管理RPM软件包,能够从指定的服务器自动下载RPM包并且安装 ...
- Kafka 安装配置
1. 下载安装kafka 下载地址:http://apache.fayea.com/kafka/ 解压安装包 tar zxvf kafka_版本号.tgz 2. 配置 修改kafka的config/s ...
- 一. 优化小程序自身的Storage
小程序中的存储只有 Storage ,特性如下: 上限为 10MB 以用户纬度隔离,同一个设备,A 无法访问 B 用户的数据. 持久缓存,只有在用户关掉小程序才会删除,如果空间不足,会进行 LRU , ...
- JavaScript中实现小数点后保留2位
在项目中有时候会遇到要求输入的数字是整数或者小数点后绑定2位小数,因此可以用.toFixed(2)方法 下面是关于toFixed()方法的demo: <input type="numb ...
- Javascript Read Excel
本文引用以下路径 https://www.cnblogs.com/liuxianan/p/js-excel.html
- H5_ 表单及其他新增和改良元素
1. form属性 formaction属性 formmethod属性 formenctype属性 formtarget属性 <!DOCTYPE html> <html lang=& ...
- dedecms 文章根据 权重排序
原文链接 一: dede:list 标签 找到 /include/arc.listview.class.php if($orderby=="senddate" || $orde ...