如何在启用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系列就基本告 ...
随机推荐
- java中的时间类型
java时间的格式 SimpleDateFormat dateFormate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Stri ...
- something for learning
[Unity Shaders]法线纹理(Normal Mapping)的实现细节 https://blog.csdn.net/candycat1992/article/details/41605257
- springboot添加邮件发送及压缩功能
springboot添加邮件发送及文件压缩功能 转载请注明出处:https://www.cnblogs.com/funnyzpc/p/9190233.html 先来一段诗 ``` 就这样吧 忍受折磨 ...
- Y1O001波分复用器
# 波分复用器## 光分波器### 波分合波器种类* 耦合型 * 光纤熔融拉锥 * 熔融拉锥法是指将两根(或两根以上)除去涂覆层的光纤以一定的方法靠拢,在高温加热下熔融,同时向两侧拉伸,最终在加热区形 ...
- MongoDB 用Robomong可视化工具操作的 一些简单语句
一.数据更新 db.getCollection('表名').update({ "字段":{$in:["值"]} }, //更新条件 {$set:{ " ...
- 正则匹配ts的引用
ts文件引入代码如下: import { IView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles } from './gridv ...
- firewalld的基本使用
参考原文链接:https://www.cnblogs.com/moxiaoan/p/5683743.html 1.firewalld的基本使用 启动: systemctl start firewall ...
- 在Linux上搭建测试环境常用命令(转自-测试小柚子)
一.搭建测试环境: 二.查看应用日志: (1)vivi/vim 原本是指修改文件,同时可以使用vi 日志文件名,打开日志文件(2)lessless命令是查看日志最常用的命令.用法:less 日志文件名 ...
- [LeetCode] Minimum Cost to Merge Stones 混合石子的最小花费
There are N piles of stones arranged in a row. The i-th pile has stones[i] stones. A move consists ...
- Python基础之面向对象2(封装)
一.封装定义: 二.作用 三.私有成员: 1.基本概念及作用 2.__slots__手段私有成员: 3.@property属性手段私有成员: 四.基础示例代码 1.用方法封装变量 "&quo ...