如何在启用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系列就基本告 ...
随机推荐
- 2018-2019 20165319 网络对抗 Exp4 恶意代码分析
基础问题回答 1. 如果在工作中怀疑一台主机上有恶意代码,但只是猜想,所有想监控下系统一天天的到底在干些什么.请设计下你想监控的操作有哪些,用什么方法来监控 答:1.使用Windows自带的schta ...
- vue小白必看的生命钩子函数图解
还有3个钩子并未出现在图上: 1.activated生命周期钩子函数在keep-alive 组件激活时调用,该钩子在服务器端渲染期间不被调用. 2.deactivated生命周期钩子函数在keep-a ...
- pycharm中range的应用
v = range(100) for item in v: print (item) #输出结果是0 1 2 3 ......99 这是在python3中实现的,python2中不一样 下面是一个从大 ...
- PS2键盘扫描码:通码与断码
键盘扫描码(实用于标准PC的101.102和104 键的键盘),按下发送通码,弹起发送断码. 说明: 第一类按键,通码为1字节,断码为 0xF0+通码 形式.如A键,其通码为 0x1C,断码为 0xF ...
- 2分钟读懂大数据框架Hadoop和Spark的异同
转自:https://www.cnblogs.com/reed/p/7730313.html 谈到大数据,相信大家对Hadoop和Apache Spark这两个名字并不陌生.但我们往往对它们的理解只是 ...
- window10 Docker仓库访问
window10 Docker仓库访问 docer官网 docker仓库 windown10 安装docker可以参考 window10安装docker 配置了加速器以后还访问不了,点击托盘处dock ...
- vue中计算属性computed方法内传参
vue中computed计算属性无法直接进行传参 如果有传参数的需求比如说做数据筛选功能可以使用闭包函数(也叫匿名函数)实现 例如: 在上篇博客vue安装使用最后的成绩表练习中的过滤功能的实现: &l ...
- XML语言2.约束
三.XML的约束 目的:编写一个文件用来约束另一个xml文件的书写规范. DTD 用来约束xml的文件格式,保证这个文件是有效的. 内部DTD:所有的约束都是直接定义在xml文件中的 内部的 DOCT ...
- Python-JSON和pickle
笔记:一:简介 (1)JSON (JavaScript Object Notation) 是一种轻量级(XML重量级)的数据交换格式. 是为了数据交换而定制的一种规则,它基于ECMAScript的一个 ...
- Bulk API
承接上文,使用Java High Level REST Client操作elasticsearch Bulk API 高级客户端提供了批量处理器以协助批量请求 Bulk Request BulkReq ...