前端无感刷新token

在这里插入图片描述
摘要:

Axios 无感知刷新令牌是一种在前端应用中实现自动刷新访问令牌(access token)的技术,确保用户在进行 API 请求时不会因为令牌过期而中断操作

目录概览

    • XMLHttpRequest
    • Axios
    • Fetch API
    • JQ
    • uni.request
    • 注意事项:

  • 访问令牌(Access Token):用于访问受保护资源的凭证,通常有一定的有效期。
  • 刷新令牌(Refresh Token):用于获取新的访问令牌,当访问令牌过期时使用。

实现步骤:

  1. 设置拦截器:在 Axios的请求拦截器中添加逻辑,检查当前时间与令牌的过期时间。如果访问令牌已过期但刷新令牌仍然有效,则调用刷新令牌接口获取新的访问令牌。
  2. 更新令牌存储:一旦获得新的访问令牌,将其存储到 localStorage、Vuex 或其他状态管理工具中,以便后续请求使用新令牌。
  3. 重试原始请求:在成功刷新令牌后,重新发送被拦截的请求,此时使用新的访问令牌。

XMLHttpRequest

// 创建 XMLHttpRequest 实例
const xhr = new XMLHttpRequest();// 登录成功后保存 Token 和 Refresh Token
function onLoginSuccess(response) {localStorage.setItem('accessToken', response.data.accessToken);localStorage.setItem('refreshToken', response.data.refreshToken);
}// 发起请求的函数
function sendRequest(url, method, data) {return new Promise((resolve, reject) => {xhr.open(method, url);xhr.setRequestHeader('Authorization', `Bearer ${localStorage.getItem('accessToken')}`);xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status === 200) {resolve(JSON.parse(xhr.responseText));} else {reject({ status: xhr.status, response: xhr.responseText });}}};if (method === 'POST' && data) {xhr.send(JSON.stringify(data));} else {xhr.send();}});
}// 刷新 Token 的函数
async function refreshToken() {const refreshToken = localStorage.getItem('refreshToken');const response = await fetch('/path/to/refresh', {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify({ refresh_token: refreshToken }),});const res = await response.json();if (res.success) {localStorage.setItem('accessToken', res.data.newAccessToken);return true; // 表示刷新成功} else {return false; // 表示刷新失败}
}// 拦截响应并处理 Token 刷新
xhr.addEventListener('readystatechange', function() {if (xhr.readyState === 4 && xhr.status === 401) {refreshToken().then(refreshed => {if (refreshed) {xhr.setRequestHeader('Authorization', `Bearer ${localStorage.getItem('accessToken')}`);xhr.send(); // 重新发送请求} else {alert('请重新登录'); // Token 刷新失败,可能需要用户重新登录}});}
});

Axios

import axios from 'axios';// 创建 Axios 实例
const apiClient = axios.create({baseURL: 'https://your-api-url.com',// 其他配置...
});// 响应拦截器
apiClient.interceptors.response.use(response => {return response;
}, error => {const { response } = error;if (response && response.status === 401) {return refreshToken().then(refreshed => {if (refreshed) {// 令牌刷新成功,重试原始请求return apiClient.request(error.config);} else {// 令牌刷新失败,可能需要用户重新登录return Promise.reject(error);}});}return Promise.reject(error);
});// 令牌刷新函数
function refreshToken() {return apiClient.post('/path/to/refresh', {// 刷新令牌所需的参数,例如 refresh_token}).then(response => {if (response.data.success) {// 假设响应数据中包含新的访问令牌const newAccessToken = response.data.newAccessToken;// 更新令牌存储localStorage.setItem('accessToken', newAccessToken);// 更新 Axios 实例的 headers,以便后续请求使用新令牌apiClient.defaults.headers.common['Authorization'] = `Bearer ${newAccessToken}`;return true; // 表示刷新成功} else {return false; // 表示刷新失败}});
}

Fetch API

// 定义一个函数来处理Fetch请求
async function fetchWithToken(url, options = {}) {const token = localStorage.getItem('token');if (token) {options.headers = {...options.headers,'Authorization': `Bearer ${token}`};}try {const response = await fetch(url, options);if (response.status === 401) { // 假设401表示令牌过期const refreshToken = localStorage.getItem('refreshToken');if (!refreshToken) {throw new Error('No refresh token available');}// 调用刷新令牌接口const refreshResponse = await fetch('/api/refresh-token', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({ refreshToken })});if (refreshResponse.ok) {const data = await refreshResponse.json();localStorage.setItem('token', data.newAccessToken);// 重新尝试原始请求options.headers['Authorization'] = `Bearer ${data.newAccessToken}`;return fetch(url, options);} else {throw new Error('Failed to refresh token');}}return response;} catch (error) {console.error('Fetch error:', error);throw error;}
}// 使用示例
fetchWithToken('/api/protected-resource').then(response => response.json()).then(data => console.log(data)).catch(error => console.error('Error:', error));
  • fetchWithToken函数: 这是一个封装了Fetch API的函数,它首先检查本地存储中的访问令牌是否存在,并在请求头中添加该令牌。如果响应状态码为401(表示令牌过期),则尝试使用刷新令牌获取新的访问令牌,并重新发送原始请求。
  • 刷新令牌逻辑: 在检测到令牌过期时,函数会调用刷新令牌接口,并将新的访问令牌存储到本地存储中。然后,它会重新设置请求头中的授权信息,并重新发送原始请求。
  • 错误处理: 如果在刷新令牌或发送请求的过程中发生错误,函数会抛出相应的错误,并在控制台中记录错误信息。

JQ

// 创建 JQuery 实例
const apiClient = $.ajaxSetup({baseURL: 'https://your-api-url.com',// 其他配置...
});// 响应拦截器
$.ajaxSetup({complete: function(jqXHR, textStatus) {if (textStatus === 'error' && jqXHR.status === 401) {return refreshToken().then(refreshed => {if (refreshed) {// 令牌刷新成功,重试原始请求return apiClient.request(this);} else {// 令牌刷新失败,可能需要用户重新登录alert('请重新登录');}});}}
});// 令牌刷新函数
function refreshToken() {return $.ajax({url: '/path/to/refresh',method: 'POST',data: {refresh_token: localStorage.getItem('refreshToken')},dataType: 'json'}).then(response => {if (response.data.success) {// 假设响应数据中包含新的访问令牌const newAccessToken = response.data.newAccessToken;// 更新令牌存储localStorage.setItem('accessToken', newAccessToken);// 更新 JQuery 实例的 headers,以便后续请求使用新令牌apiClient.defaults.headers.common['Authorization'] = `Bearer ${newAccessToken}`;return true; // 表示刷新成功} else {return false; // 表示刷新失败}});
}

uni.request

// 导入封装的request插件
import http from './interface';
import { getRefreshToken } from '@/common/api/apis.js'; // 刷新token接口let isRefreshing = false; // 是否处于刷新token状态中
let fetchApis = []; // 失效后同时发送请求的容器
let refreshCount = 0; // 限制无感刷新的最大次数function onFetch(newToken) {refreshCount += 1;if (refreshCount === 3) {refreshCount = 0;fetchApis = [];return Promise.reject();}fetchApis.forEach(callback => {callback(newToken);});// 清空缓存接口fetchApis = [];return Promise.resolve();
}// 响应拦截器
http.interceptor.response((response) => {if (response.config.loading) {uni.hideLoading();}// 请求成功但接口返回的错误处理if (response.data.statusCode && +response.data.statusCode !== 200) {if (!response.config.needPromise) {console.log('error', response);uni.showModal({title: '提示',content: response.data.message,showCancel: false,confirmText: '知道了'});// 中断return new Promise(() => {});} else {// reject Promisereturn Promise.reject(response.data);}}return response;
}, (error) => {const token = uni.getStorageSync('token');const refreshToken = uni.getStorageSync('refreshToken');// DESC: 不需要做无感刷新的白名单接口const whiteFetchApi = ['/dealersystem/jwtLogin', '/dealersystem/smsLogin', '/sso2/login', '/dealersystem/isLogin'];switch (error.statusCode) {case 401:case 402:if (token && !whiteFetchApi.includes(error.config.url)) {if (!isRefreshing) {isRefreshing = true;getRefreshToken({ refreshToken }).then(res => {let newToken = res.data;onTokenFetched(newToken).then(res => {}).catch(err => {// 超过循环次数时,回到登录页,这里可以添加你执行退出登录的逻辑uni.showToast({ title: '登录失效,请重新登录', icon: 'error' });setTimeout(() => {uni.reLaunch({ url: '/pages/login/login' });}, 1500);});}).catch(err => {// refreshToken接口报错,证明refreshToken也过期了,那没办法啦重新登录呗uni.showToast({ title: '登录失效,请重新登录', icon: 'error' });setTimeout(() => {uni.reLaunch({ url: '/pages/login/login' });}, 1500);}).finally(() => { isRefreshing = false });}return new Promise((resolve) => { // 此处的promise很关键,就是确保你的接口返回值在此处resolve,以便后续代码执行addFetchApi((newToken) => {error.config.header['Authorization'] = `Bearer ${newToken}`;http.request(error.config).then(response => {resolve(response);});});});}break;default:break;}
});

注意事项:

  • 错误处理:确保在刷新令牌失败时,有适当的错误处理机制,例如提示用户重新登录。
  • 并发请求:处理多个请求同时需要刷新令牌的情况,避免重复刷新。
  • 安全性:确保刷新令牌的安全存储和传输,防止被恶意攻击者获取。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/61091.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32 独立看门狗(IWDG)详解

目录 一、引言 二、独立看门狗的作用 三、独立看门狗的工作原理 1.时钟源 2.计数器 3.喂狗操作 4.超时时间计算 5.复位机制 四、独立看门狗相关寄存器 1.键寄存器(IWDG_KR) 2.预分频寄存器(IWDG_PR) 3.重载寄存器&…

RHCE的练习(12)

写一个脚本,完成以下要求: 给定一个用户: 如果其UID为0,就显示此为管理员;否则,就显示其为普通用户; #!/bin/bash ​ # 使用read命令获取用户名 read -p "请输入用户名: " username ​…

游戏引擎学习第15天

视频参考:https://www.bilibili.com/video/BV1mbUBY7E24 关于游戏中文件输入输出(IO)操作的讨论。主要分为两类: 只读资产的加载 这部分主要涉及游戏中用于展示和运行的只读资源,例如音乐、音效、美术资源(如 3D 模型和…

探索 HTML 和 CSS 实现的 3D旋转相册

效果演示 这段HTML与CSS代码创建了一个包含10张卡片的3D旋转效果&#xff0c;每张卡片都有自己的边框颜色和图片。通过CSS的3D变换和动画&#xff0c;实现了一个动态的旋转展示效果 HTML <div class"wrapper"><div class"inner" style"-…

什么是Hadoop

Hadoop 介绍 Hadoop 是由 Apache 开发的开源框架&#xff0c;用于处理分布式环境中的海量数据。Hadoop 使用 Java 编写&#xff0c;通过简单的编程模型允许在集群中进行大规模数据集的存储和计算。它具备高可靠性、容错性和扩展性。 分布式存储&#xff1a;Hadoop 支持跨集群…

逆向攻防世界CTF系列39-debug

逆向攻防世界CTF系列39-debug 查了资料说.NET要用其它调试器&#xff0c;下载了ILSPY和dnSPY ILSPY比较适合静态分析代码最好了&#xff0c;函数名虽然可能乱码不显示&#xff0c;但是单击函数名还是能跟踪的&#xff0c;而dnSPY在动态调试上效果好&#xff0c;它的函数名不仅…

华为开源自研AI框架昇思MindSpore应用案例:人体关键点检测模型Lite-HRNet

如果你对MindSpore感兴趣&#xff0c;可以关注昇思MindSpore社区 一、环境准备 1.进入ModelArts官网 云平台帮助用户快速创建和部署模型&#xff0c;管理全周期AI工作流&#xff0c;选择下面的云平台以开始使用昇思MindSpore&#xff0c;获取安装命令&#xff0c;安装MindSpo…

Cellebrite VS IOS18Rebooting

Cellebrite VS IOS18Rebooting我们想分享一些有关 iOS 18 重启“功能”的信息。在过去一周左右的时间里&#xff0c;人们对 iOS 18 中一项新的未记录功能产生了极大关注&#xff0c;该功能会导致设备在一段时间不活动后重新启动。 这意味着&#xff0c;如果设备在一定时间不活…

使用 Axios 拦截器优化 HTTP 请求与响应的实践

目录 前言1. Axios 简介与拦截器概念1.1 Axios 的特点1.2 什么是拦截器 2. 请求拦截器的应用与实践2.1 请求拦截器的作用2.2 请求拦截器实现 3. 响应拦截器的应用与实践3.1 响应拦截器的作用3.2 响应拦截器实现 4. 综合实例&#xff1a;一个完整的 Axios 配置5. 使用拦截器的好…

【最大子矩阵——双指针 / 二分】

题目 双指针&#xff1a; 代码 #include <bits/stdc.h> using namespace std; const int N 85, M 1e510; int g[N][M]; int n, m, lim; int ans 1; int main() {ios::sync_with_stdio(0);cin.tie(0);cin >> n >> m;for(int i 1; i < n; i)for(int …

内网渗透-隧道判断-SSH-DNS-icmp-smb-上线linux-mac

1.通道判断 #SMB 隧道&通讯&上线 判断&#xff1a;445 通讯 上线&#xff1a;借助通讯后绑定上线 通讯&#xff1a;直接 SMB 协议通讯即可 #ICMP 隧道&通讯&上线 判断&#xff1a;ping 命令 上线&#xff1a;见前面课程 通讯&#xff1a;其他项…

【优选算法篇】分治乾坤,万物归一:在重组中窥见无声的秩序

文章目录 分治专题&#xff08;二&#xff09;&#xff1a;归并排序的核心思想与进阶应用前言、第二章&#xff1a;归并排序的应用与延展2.1 归并排序&#xff08;medium&#xff09;解法&#xff08;归并排序&#xff09;C 代码实现易错点提示时间复杂度和空间复杂度 2.2 数组…

【微软:多模态基础模型】(3)视觉生成

欢迎关注【youcans的AGI学习笔记】原创作品 【微软&#xff1a;多模态基础模型】&#xff08;1&#xff09;从专家到通用助手 【微软&#xff1a;多模态基础模型】&#xff08;2&#xff09;视觉理解 【微软&#xff1a;多模态基础模型】&#xff08;3&#xff09;视觉生成 【微…

netcore Kafka

一、新建项目KafakDemo <ItemGroup><PackageReference Include"Confluent.Kafka" Version"2.6.0" /></ItemGroup> 二、Program.cs using Confluent.Kafka; using System; using System.Threading; using System.Threading.Tasks;names…

工业生产安全-安全帽第一篇-opencv及java开发环境搭建

一.背景 公司是非煤采矿业&#xff0c;核心业务是采选&#xff0c;大型设备多&#xff0c;安全风险因素多。当下政府重视安全&#xff0c;头部技术企业的安全解决方案先进但价格不低&#xff0c;作为民营企业对安全投入的成本很敏感。利用我本身所学&#xff0c;准备搭建公司的…

fastadmin多个表crud连表操作步骤

1、crud命令 php think crud -t xq_user_credential -u 1 -c credential -i voucher_type,nickname,user_id,voucher_url,status,time --forcetrue2、修改控制器controller文件 <?phpnamespace app\admin\controller;use app\common\controller\Backend;/*** 凭证信息…

【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,2-26

文件下载与邀请翻译者 学习英特尔开发手册&#xff0c;最好手里这个手册文件。原版是PDF文件。点击下方链接了解下载方法。 讲解下载英特尔开发手册的文章 翻译英特尔开发手册&#xff0c;会是一件耗时费力的工作。如果有愿意和我一起来做这件事的&#xff0c;那么&#xff…

Essential Cell Biology--Fifth Edition--Chapter one (8)

1.1.4.6 The Cytoskeleton [细胞骨架] Is Responsible for Directed Cell Movements 细胞质基液不仅仅是一种无结构的化学物质和细胞器的混合物[soup]。在电子显微镜下&#xff0c;我们可以看到真核细胞的细胞质基液是由长而细的丝交叉而成的。通常[Frequently]&#xff0c;可…

RK3568 Linux 系统加系统运行指示灯

一、dts配置 gpio-leds {status = "okay";compatible = "gpio-leds";work-led {gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>

C++11(六)----包装器function和bind

文章目录 包装器&#xff1a;function包装器&#xff1a;bind 包装器&#xff1a;function function接口介绍 在头文件<functional>中 语法&#xff1a;function的语法比较特殊 function<返回值(参数)> 自定义变量名 要被包装的可调用对象 class Plus { public:…