ASP.NET Core中 JWT 实现无感刷新Token
在 Web 应用开发中,用户登录状态的管理至关重要。为了避免用户频繁遇到登录过期的问题,我们可以通过实现 JWT(JSON Web Token)刷新机制来提升用户体验
推荐: 使用 Refresh Token(双 Token 机制)
1. 生成和使用双 Token
通常会生成两种 Token:访问 Token (Access Token) 和 刷新 Token (Refresh Token)。
-
• 访问 Token:用于客户端与服务器之间的身份验证,有效期较短(例如 30 分钟),以提高安全性。
-
• 刷新 Token:用于获取新的访问 Token,有效期较长(例如 7 天),存储在客户端 。
Tokenservice.cs:
// 生成 JWT Access Token 和 Refresh Token
public (string AccessToken, string RefreshToken) GenerateTokens(string userId, string userName)
{var tokenHandler = new JwtSecurityTokenHandler();var key = Encoding.ASCII.GetBytes(_configuration["JwtSettings:Secret"]);// 生成 Access Tokenvar accessTokenDescriptor = new SecurityTokenDescriptor{Subject = new ClaimsIdentity(new Claim[]{new Claim(ClaimTypes.NameIdentifier, userId),new Claim(ClaimTypes.Name, userName)}),Issuer = _configuration["JwtSettings:Issuer"],Audience = _configuration["JwtSettings:Audience"],Expires = DateTime.UtcNow.AddMinutes(double.Parse(_configuration["JwtSettings:ExpireMinutes"])),SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)};var accessToken = tokenHandler.CreateToken(accessTokenDescriptor);// 生成 Refresh Tokenvar refreshTokenDescriptor = new SecurityTokenDescriptor{Subject = new ClaimsIdentity(new Claim[]{new Claim(ClaimTypes.NameIdentifier, userId)}),Issuer = _configuration["JwtSettings:Issuer"],Audience = _configuration["JwtSettings:Audience"],Expires = DateTime.UtcNow.AddDays(7), // Refresh Token 有效期 7 天SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)};var refreshToken = tokenHandler.CreateToken(refreshTokenDescriptor);return (tokenHandler.WriteToken(accessToken), tokenHandler.WriteToken(refreshToken));
}// 根据旧 JWT 令牌换取新 JWT 令牌public string ExchangeJwtToken(string oldToken){if (!ValidateJwtToken(oldToken)){thrownew InvalidOperationException("Invalid or expired token");}var principal = ParseJwtToken(oldToken);var userId = principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;var userName = principal.FindFirst(ClaimTypes.Name)?.Value;if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(userName)){thrownew InvalidOperationException("Invalid token claims");}return GenerateJwtToken(userId, userName);}
AuthController.cs
private readonly TokenService _tokenService;
privatereadonly IAppUser _appUser;public AuthController(IConfiguration configuration, IAppUser appUser, TokenService tokenService)
{_tokenService = tokenService;_appUser = appUser;
}[HttpPost("login")]
[AllowAnonymous]
public IActionResult Login([FromBody] LoginRequest loginRequest)
{var token = _tokenService.GenerateTokens(loginRequest.Username, loginRequest.Username);return Ok(new { token.RefreshToken,token.AccessToken });
}
2. 前端请求拦截器自动刷新 Token
在前端应用中,可以使用请求拦截器来自动处理 Token 刷新逻辑。当访问 Token 过期时,拦截器会自动调用刷新接口获取新的访问 Token,并重新发起请求
// 请求拦截器
axios.interceptors.request.use(
config => {const token = localStorage.getItem('accessToken');if (token) {config.headers['Authorization'] = `Bearer ${token}`;}return config;},
error => {returnPromise.reject(error);}
);axios.interceptors.response.use(
response => response,
async error => {const originalRequest = error.config;if (error.response.status === 401 && !originalRequest._retry) {originalRequest._retry = true;const refreshToken = localStorage.getItem('refreshToken');const res = await axios.post('/api/auth/refresh-token', { refreshToken });localStorage.setItem('accessToken', res.data.accessToken);returnaxios(originalRequest);}returnPromise.reject(error);}
);
3. 后端提供刷新 Token 的接口
专门的接口来处理刷新 Token 的请求。该接口会验证刷新 Token 的有效性,并返回新的访问 Token
// 刷新 Access Token
[HttpPost("refresh")]
public IActionResult RefreshToken([FromBody] RefreshTokenRequest request)
{try{var validatetoken = _tokenService.ValidateJwtToken(request.RefreshToken);if (validatetoken){return Ok(_tokenService.ExchangeJwtToken(request.RefreshToken));}return Unauthorized("Invalid refresh token");}catch (Exception ex){return Unauthorized("Invalid refresh token");}
}
不推荐:中间件自动刷新Token
-
1. 创建中间件:创建一个中间件,用于检查每个请求的JWT Token过期时间。
-
2. 读取Token:中间件读取请求头中的Authorization字段,获取JWT Token。
-
3. 检查过期时间:判断Token的过期时间,如果距离过期时间在一定范围内(如30分钟内),则生成一个新的JWT Token,并通过自定义的响应头(如X-Refresh-Token)返回给客户端。
-
4. 客户端更新Token:客户端检查响应头,如果存在X-Refresh-Token,则用新Token替换旧Token,并在后续请求中使用新Token。
-
5. 弊端:虽然实现起来相对简单,但安全性相对较低,因为Token的刷新是在客户端自动进行的,如果Token被盗用,攻击者可能会在Token过期前一直使用该Token
因为存在上述弊端,不推荐使用中间件自动刷新 Token,故不提供相关代码实现。
相关文章:
ASP.NET Core中 JWT 实现无感刷新Token
在 Web 应用开发中,用户登录状态的管理至关重要。为了避免用户频繁遇到登录过期的问题,我们可以通过实现 JWT(JSON Web Token)刷新机制来提升用户体验 推荐: 使用 Refresh Token(双 Token 机制) 1. 生成和…...
函数(函数的概念、库函数、自定义函数、形参和实参、return语句、数组做函数参数、嵌套调用和链式访问、函数的声明和定义、static和extern)
一、函数的概念 •C语⾔中的函数:⼀个完成某项特定的任务的⼀⼩段代码 •函数又被翻译为子函数(更准确) •在C语⾔中我们⼀般会⻅到两类函数:库函数 ⾃定义函数 二、库函数 1 .标准库和头文件 •C语⾔的国际标准ANSIC规定了⼀…...
物联网在烟草行业的应用
物联网技术在烟草行业的应用 物联网技术在烟草行业的应用主要体现在以下几个方面: 智能制造 :物联网技术可以实现对生产过程中的关键参数进行实时监测,确保产品的质量稳定可靠。同时,通过对设备的远程维护和故障诊断,…...
第6章:Python TDD实例变量私有化探索
写在前面 这本书是我们老板推荐过的,我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后,我突然思考,对于测试开发工程师来说,什么才更有价值呢?如何让 AI 工具更好地辅助自己写代码,或许…...
Java操作Excel导入导出——POI、Hutool、EasyExcel
目录 一、POI导入导出 1.数据库导出为Excel文件 2.将Excel文件导入到数据库中 二、Hutool导入导出 1.数据库导出为Excel文件——属性名是列名 2.数据库导出为Excel文件——列名起别名 3.从Excel文件导入数据到数据库——属性名是列名 4.从Excel文件导入数据到数据库…...
BUUCTF_Web([GYCTF2020]Ezsqli)
1.输入1 ,正常回显。 2.输入1 ,报错false,为字符型注入,单引号闭合。 原因: https://mp.csdn.net/mp_blog/creation/editor/145170456 3.尝试查询字段,回显位置,数据库,都是这个。…...
微软宣布Win11 24H2进入新阶段!设备将自动下载更新
快科技1月19日消息,微软于1月16日更新了支持文档,宣布Windows 11 24H2进入新阶段。 24H2更新于2024年10月1日发布,此前为可选升级,如今微软开始在兼容的Windows 11设备上自动下载并安装24H2版本。 微软表示:“运行Wi…...
SpringBoot:解决前后端请求跨域问题(详细教程)
文章目录 一、前言二、解决方式 2.1 使用 CrossOrigin 注解(简单方便,适用于单个或少量接口)2.2 全局配置跨域(适用于整个项目中大量接口都需要跨域的情况)2.3 使用过滤器来处理跨域(更底层的实现方式&…...
Android-V lmkd 中的那些属性值
源码基于:Android V 相关博文: Android lmkd 机制详解(一) Android lmkd 机制详解(二) Android lmkd 机制从R到T 1. 汇总 属性名说明默认值 ro.lmk.debug 启动 lmkd 的debug 模式,会打印一…...
PageHelper快速使用
依赖 <!--分页插件PageHelper--> <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.7</version> </dependency>示例 /** * 封装分页结果…...
图像处理基础(3):均值滤波器及其变种
均值滤波器可以归为低通滤波器,是一种线性滤波器,其输出为邻域模板内的像素的简单平均值,主要用于图像的模糊和降噪。 均值滤波器的概念非常的直观,使用滤波器窗口内的像素的平均灰度值代替图像中的像素值,这样的结果就…...
力扣刷题心得_JAVA
数学 > 数组 > 链表 > 字符串 > 哈希表 > 双指针 > 递归 > 栈 > 队列 > 树 //一般力扣中传入的参数和新建的对象作为返回值,都不列入空间复杂度中 //但是面试的时候要和面试官商量好,灵活定义空间复杂度 //当然最好是就在传入的对象作为返回值,(在原…...
音乐播放器实现:前端HTML,CSS,JavaScript综合大项目
音乐播放器实现:前端HTML,CSS,JavaScript综合大项目 项目概述项目视图效果一、侧边栏相关代码(一)HTML代码(二)css代码 二、登录页面(一)HTML代码(二)css代码…...
Unity编辑器缩放设置
Unity默认界面UI字体太小了,可以设置一下缩放 打开首选项, UI Scaling 设置成125%或者更大 ,然后重启...
ChatGPT大模型极简应用开发-CH1-初识 GPT-4 和 ChatGPT
文章目录 1.1 LLM 概述1.1.1 语言模型和NLP基础1.1.2 Transformer及在LLM中的作用1.1.3 解密 GPT 模型的标记化和预测步骤 1.2 GPT 模型简史:从 GPT-1 到 GPT-41.2.1 GPT11.2.2 GPT21.2.3 GPT-31.2.4 从 GPT-3 到 InstructGPT1.2.5 GPT-3.5、Codex 和 ChatGPT1.2.6 …...
Golang学习笔记_27——单例模式
Golang学习笔记_24——泛型 Golang学习笔记_25——协程Golang学习笔记_25——协程 Golang学习笔记_26——通道 文章目录 单例模式1. 介绍2. 应用场景3. 实现3.1 饿汉式3.2 懒汉模式 源码 单例模式 1. 介绍 单例模式是一种创建型设计模式,它确保一个类只有一个实例…...
хорошо哈拉少wordpress俄语主题
хорошо哈拉少wordpress俄语主题 wordpress俄文网站模板,推荐做俄罗斯市场的外贸公司建俄语独立站使用。 演示 https://www.jianzhanpress.com/?p7360...
[数据结构与算法]js实现二叉树
DFS 与 BFS dfs 递归 本质通过栈结构 bfs 层序遍历 通过队列结构 function permute(nums) {let res [];let cur []; // 记录当前内容let visted {}; //记录访问过的节点let len nums.length;function dfs(nth) {//递归终止条件if (nth len) {res.push([...cur]);return …...
MySQL程序之:连接到服务器的命令选项
本节介绍大多数MySQL客户端程序支持的选项,这些选项控制客户端程序如何建立与服务器的连接、连接是否加密以及连接是否压缩。这些选项可以在命令行或选项文件中给出。 连接建立的命令选项 本节介绍控制客户端程序如何建立与服务器的连接的选项。 表6.4连接建立选…...
python3GUI--仿崩坏三二次元登录页面(附下载地址) By:PyQt5
文章目录 一.前言二.预览三.实现方案1.实现原理1.PyQt52. 具体实现 2.UI设计1.UI组件化、模块化2.UI设计风格思路 3.项目代码结构4.使用方法3.代码分享1.支持跳转网页的QLabel组件2.三角形ICON按钮 四.总结 大小:33.3 …...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7
在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤: 第一步: 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为: // 改为 v…...
java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...
【版本控制】GitHub Desktop 入门教程与开源协作全流程解析
目录 0 引言1 GitHub Desktop 入门教程1.1 安装与基础配置1.2 核心功能使用指南仓库管理日常开发流程分支管理 2 GitHub 开源协作流程详解2.1 Fork & Pull Request 模型2.2 完整协作流程步骤步骤 1: Fork(创建个人副本)步骤 2: Clone(克隆…...
