文章目录
前景提要:
背景
无感刷新 token 一般指的是使用 refresh token 无感刷新 access token。
基本思路
设置全局请求拦截器,从 localstorage 或其他地方获取 token 放在请求头中携带。在响应拦截器中,判断响应结果中是否有 token,有就存下来放在 localstorage 或其他地方。
一句话总结就是本地有就带,响应有就存。
实现自动刷新,就是在响应拦截之前的基础上再加一个判断,如果 access token 过期了,就携带 refresh token 去请求认证中心的接口。拿到新的 access token 后,再次对业务接口发起请求。
- 注意别忘了要让新业务请求携带最新的 access token。
在axios拦截器中重新发起请求,就是拿到业务请求的 config,用axios实例发起请求。所以也可以说,一个请求的本质就是它的config配置对象。
src\api\http\token.ts
import {
AxiosResponse, InternalAxiosRequestConfig } from "axios";
import httpRequest from "..";
import {
refreshAccessToken } from "../modules/refreshToken";
export const ACCESS_TOKEN_KEY = "access_token";
export const REFRESH_TOKEN_KEY = "refresh_token";
export const UNAUTHORIZED_STATUS_CODE = 401;
// token 工具函数
/**
* 获取token
* @param key token的key
* @returns {string}accessToken
*/
export function getToken(key: string) {
return localStorage.getItem(key);
}
/**
* 存储token到本地
* @param key token的key
* @param token token的值
*/
export function setToken(key: string, token: string) {
localStorage.setItem(key, token);
}
// 拦截器
/**
* 请求拦截器:只要本地有access token,所有请求的请求头就携带上它。(对于没有采用单点登录方案的系统,access token就是普通 token)
* @param {InternalAxiosRequestConfig}config
* @returns {InternalAxiosRequestConfig}config
*/
export function setAccessTokenRequestInterceptor(config: InternalAxiosRequestConfig) {
if (config.headers) config.headers.authorization = `Bearer ${
getToken("accessToken")}`;
return config;
}
/**
* 响应拦截器:只要服务器响应了 token,就保存下来到本地,无论是 access token 还是 refresh token。
* 当接口因权限拒绝或者本地计算 access token 过期,则就去拿 refresh token 无感刷新 access token。
* @param {AxiosResponse}res
* @returns {AxiosResponse}res
*/
export async function getTokenResponseInterceptor(res: AxiosResponse) {
// 假设服务器将token放在响应头中返回
// 保存授权 token,也就是 access token 或者普通的 token
if (res.headers.authorization) {
const token = res.headers.authorization.repalce("Bearer ", "");
setToken(ACCESS_TOKEN_KEY, token);
}
// 保存 refresh token
if (res.headers.refreshtoken) {
const refreshToken = res.headers.refreshtoken.repalce("Bearer ", "");
setToken(REFRESH_TOKEN_KEY, refreshToken);
}
// 请求业务接口没有权限,说明 access token 过期,需要刷新 token
if (res.data.code === UNAUTHORIZED_STATUS_CODE) {
// 请求服务器获取最新access token,当前拦截器递归保存token
const isRefreshSuccess = await refreshAccessToken();
if (isRefreshSuccess) {
// 刷新 access token 成功,装配新 access token 后拿到 axios 实例重新发起请求
res.config.headers.Authorization = `Bearer ${
getToken(ACCESS_TOKEN_KEY)}`;
const response = await httpRequest.getInstance().request(res.config);
return response