当前位置: 首页 > news >正文

无感刷新token

无感刷新

无感刷新Token技术是一种用于实现持久登录体验的关键技术,它通过在用户登录后自动刷新Token,以延长用户的登录状态,避免频繁要求用户重新登录。

实现

使用access_token(短效token)和refresh_token(长效token),当请求拦截判断属于access_token过期时,使用refresh_token获取一组新的token,并且将所有无效请求入队列,等拿到有效token时依次出队列重新请求。

后端

使用nestjs写了几个测试用的接口

controller.ts

import { Body, Query, Controller,Request, Get, Post, BadRequestException, Inject, Req, UnauthorizedException } from '@nestjs/common';
import { AppService } from './app.service';
import { UserDto } from './dto/user.dto';
import { JwtService } from "@nestjs/jwt"
const users = [{ username:"admin", password:"admin" },{ username:"zhangsan", password:"123" },
]@Controller()
export class AppController {constructor(private readonly appService: AppService) {}@Inject(JwtService)private jwtService:JwtService;@Get()getHello(): string {return this.appService.getHello();}@Post('login')login(@Body() userDto : UserDto){const user = users.find(item => item.username === userDto.username);if(!user) {throw new BadRequestException('用户不存在');}if(user.password !== userDto.password) {throw new BadRequestException("密码错误");}const accessToken = this.jwtService.sign({username:user.username,}, {expiresIn: '0.001h'})const refreshToken =  this.jwtService.sign({username:user.username,}, {expiresIn: '7d'})return {userInfo: {username: user.username,},accessToken: accessToken,refreshToken: refreshToken};}@Get("aaa")aaa(@Req() req: Request){const authorization = req.headers['authorization'];if(!authorization){throw new UnauthorizedException("用户未登录")}try {const token = authorization;const data = this.jwtService.verify(token);console.log(data);return 'aaa'} catch (error) {throw new UnauthorizedException("token失效,请重新登录")}}@Get("bbb")bbb(@Req() req: Request){const authorization = req.headers['authorization'];if(!authorization){throw new UnauthorizedException("用户未登录")}try {const token = authorization;const data = this.jwtService.verify(token);console.log(data);return 'bbb'} catch (error) {throw new UnauthorizedException("token失效,请重新登录")}}@Get('refresh')refresh(@Query('token') token: string) {try{const data = this.jwtService.verify(token);const user = users.find(item => item.username === data.username);const accessToken = this.jwtService.sign({username: user.username,}, {expiresIn: '0.001h'});const refreshToken = this.jwtService.sign({username: user.username}, {expiresIn: '7d'})return {accessToken,refreshToken};} catch(e) {throw new UnauthorizedException('token 失效,请重新登录');}}}

其中login生成 access_token 和 refresh_token

前端请求封装

import axios from 'axios';
import { userStore } from "@/store/user.js"
const store = userStore();
class RequestQueue {constructor() {this.queue = [];this.isRefresh = false;}// 入队enqueue(value) {return this.queue.push(value);}// 出队dequeue() {return this.queue.shift();}// 取队头元素peek() {return this.queue[0];}// 判断队列是否为空isEmpty() {return this.queue.length === 0;}// 取队列有多少个元素size() {return this.queue.length;}// 清空队列clear() {this.queue = [];}setIsRefresh(isRefresh){return this.isRefresh = isRefresh}
}
//1. 创建axios对象
const service = axios.create({baseURL: import.meta.env.VITE_APP_BASE_API,// 超时timeout: 10000});
//2. 请求拦截器
service.interceptors.request.use(config => {console.log('store拦截信息',store)if(store.accessToken){config.headers.Authorization = store.accessToken;}return config;
}, error => {Promise.reject(error);
});let requests = new RequestQueue;  // 请求队列
//3. 响应拦截器
service.interceptors.response.use(response => {//判断code码return response.data;
},error => {if(error.response.status == 401){// token过期处理// 请求入队requests.enqueue(error.response.config);if(!requests.isRefresh){requests.setIsRefresh(true)store.doRefreshToken().then(res=>{while(!requests.isEmpty()){let config = requests.dequeue()config.Authorization = store.accessTokenservice.request(config)}requests.setIsRefresh(false)});}} else {// token请求没有过期return new Promise((resolve,reject)=>{reject(error)});}
});
export default service;

相关文章:

  • 【hcie-cloud】【3】华为云Stack规划设计之华为云Stack交付综述【上】
  • openlayers
  • Flink—— Data Source 介绍
  • Flask(Jinja2) 服务端模板注入漏洞(SSTI)
  • Canal常见面试题
  • opencv4笔记
  • TinyMce富文本编辑器使用【详细】
  • ubuntu22.04桌面版系统无法识别USB摄像头
  • 社区团购商品数据抓取
  • 105.am40刷机(linux)折腾记1-前期的准备工作1
  • ECharts常用配置
  • LeetCode_多源 BFS_中等_2258.逃离火灾
  • 安卓学习记录
  • 达梦主备部署
  • 【OpenCV实现图像:用OpenCV图像处理技巧之白平衡算法】
  • 5、React组件事件详解
  • const let
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • iOS编译提示和导航提示
  • JavaScript-Array类型
  • node.js
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • Web Storage相关
  • 猫头鹰的深夜翻译:Java 2D Graphics, 简单的仿射变换
  • 如何选择开源的机器学习框架?
  • 小程序测试方案初探
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • 湖北分布式智能数据采集方法有哪些?
  • ​html.parser --- 简单的 HTML 和 XHTML 解析器​
  • $redis-setphp_redis Set命令,php操作Redis Set函数介绍
  • (1)(1.9) MSP (version 4.2)
  • (C++17) optional的使用
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (规划)24届春招和25届暑假实习路线准备规划
  • (未解决)macOS matplotlib 中文是方框
  • ***微信公众号支付+微信H5支付+微信扫码支付+小程序支付+APP微信支付解决方案总结...
  • .gitignore文件—git忽略文件
  • .NET Framework .NET Core与 .NET 的区别
  • .NET Standard 支持的 .NET Framework 和 .NET Core
  • .Net 中Partitioner static与dynamic的性能对比
  • /usr/bin/perl:bad interpreter:No such file or directory 的解决办法
  • @Import注解详解
  • [1204 寻找子串位置] 解题报告
  • [52PJ] Java面向对象笔记(转自52 1510988116)
  • [Apio2012]dispatching 左偏树
  • [AutoSar NVM] 存储架构
  • [C/C++]关于C++11中的std::move和std::forward
  • [C/C++]数据结构----顺序表的实现(增删查改)
  • [CSS3备忘] transform animation 等
  • [HJ56 完全数计算]
  • [js]- 两个对象的合并(Object.assign)
  • [leetcode] 66. 加一
  • [Machine Learning][Part 7]神经网络的基本组成结构
  • [mmucache]-ARMV8-aarch64的虚拟内存(mmutlbcache)介绍-概念扫盲
  • [node] Node.js的Web 模块