前端无感刷新token机制(一文说明白)

前言

用户登录之后,会返回一个用户的标识,之后带上这个标识请求别的接口,就能识别出该用户。

标识登录状态的方案有两种: session 和 jwt。这两种方案一个服务端存储,通过 cookie 携带标识,一个在客户端存储,通过 header 携带标识。

session 是通过 cookie 返回一个 id,关联服务端内存里保存的 session 对象,请求时服务端取出 cookie 里 id 对应的 session 对象,就可以拿到用户信息。

jwt 不在服务端存储,会直接把用户信息放到 token 里返回,每次请求带上这个 token,服务端就能从中取出用户信息。

session 的方案默认不支持分布式,因为是保存在一台服务器的内存的,另一台服务器没有。jwt 的方案天然支持分布式,因为信息保存在 token 里,只要从中取出来就行。

为什么需要无感刷新token机制

服务端把用户信息放入 token 里,设置一个过期时间,客户端请求的时候通过 authorization 的 header 携带 token,服务端验证通过,就可以从中取到用户信息。

但是token 是有过期时间的,比如 3 天,那过期后再访问就需要重新登录了。这样体验并不好。

想想你在用某个 app 的时候,用着用着突然跳到登录页了,告诉你需要重新登录了。是不是体验很差?

所以要加上续签机制,也就是延长 token 过期时间。

主流的方案是通过双 token,一个 access_token、一个 refresh_token(一个短token,一个长token)。

无感刷新token机制

用户登录成功之后,两个 token(一个 access_token、一个 refresh_token),访问接口时携带 access_token 访问,当 access_token 过期时,通过 refresh_token 来刷新,拿到新的 access_token 和 refresh_token。

而 access_token 一般过期时间设置的比较短,比如 30 分钟,refresh_token 设置的过期时间比较长,比如 7 天。这样,只要你 7 天内访问一次,就能刷新 token,再续 7 天,一直不需要登录。

但如果你超过 7 天没访问,那 refresh_token 也过期了,就需要重新登录了。想想你常用的 APP,是不是没再重新登录过?而不常用的 APP,再次打开是不是就又要重新登录了?这种一般都是双 token 做的。

实现

在axios的响应拦截器中刷新token。

这里还需要排除下 /refresh 接口,也就是刷新失败不继续刷新,不然会进入死循环。

刷新 token 成功,就重发之前的请求,否则,提示重新登录。其他错误直接返回。

在刷新 token 的接口里,拿到新的 access_token 和 refresh_token 后,更新本地存储的 token。

js 复制代码
axiosInstance.interceptors.response.use(
    (response) => {
        return response;
    },
    async (error) => {
        let { data, config } = error.response;

        if (data.statusCode === 401 && !config.url.includes('/refresh')) {
            
            const res = await refreshToken();

            if(res.status === 200) {
                return axiosInstance(config);
            } else {
                alert(data || '登录过期,请重新登录');
            }
        } else {
            return error.response;
        }
    }
)

async function refreshToken() {
    const res = await axiosInstance.get('/refresh', {
        params: {
          token: localStorage.getItem('refresh_token')
        }
    });
    localStorage.setItem('access_token', res.data.accessToken);
    localStorage.setItem('refresh_token', res.data.refreshToken);
    
    return res;
}

但是还有一些问题,如果并发请求,多次调用后端接口,会刷新token多次,解决方案如下:

加一个 refreshing 的标记,记录是否正在刷新token,如果在刷新,那就返回一个 promise,并且把它的 resolve 方法还有 config 加入到一个队列里。

当 token刷新 成功之后,重新发送队列中的请求(即在刷新期间积压的请求),并且把结果通过 resolve 返回(即重新发起请求)。

js 复制代码
interface PendingTask {
    config: AxiosRequestConfig
    resolve: Function
}

let refreshing = false;
const queue: PendingTask[] = [];

axiosInstance.interceptors.response.use(
    (response) => {
        return response;
    },
    async (error) => {
        let { data, config } = error.response;

        if(refreshing) {
            return new Promise((resolve) => {
                queue.push({
                    config,
                    resolve
                });
            });
        }

        if (data.statusCode === 401 && !config.url.includes('/refresh')) {
            refreshing = true;
            
            const res = await refreshToken();

            refreshing = false;

            if(res.status === 200) {

                queue.forEach(({config, resolve}) => {
                    resolve(axiosInstance(config))
                })

                return axiosInstance(config);
            } else {
                alert(data || '登录过期,请重新登录');
            }
        } else {
            return error.response;
        }
    }
)

axiosInstance.interceptors.request.use(function (config) {
    const accessToken = localStorage.getItem('access_token');

    if(accessToken) {
        config.headers.authorization = 'Bearer ' + accessToken;
    }
    return config;
})
相关推荐
前端拾光者4 分钟前
利用D3.js实现数据可视化的简单示例
开发语言·javascript·信息可视化
Json_1817901448022 分钟前
电商拍立淘按图搜索API接口系列,文档说明参考
前端·数据库
风尚云网1 小时前
风尚云网前端学习:一个简易前端新手友好的HTML5页面布局与样式设计
前端·css·学习·html·html5·风尚云网
木子02041 小时前
前端VUE项目启动方式
前端·javascript·vue.js
GISer_Jing1 小时前
React核心功能详解(一)
前端·react.js·前端框架
捂月1 小时前
Spring Boot 深度解析:快速构建高效、现代化的 Web 应用程序
前端·spring boot·后端
深度混淆1 小时前
实用功能,觊觎(Edge)浏览器的内置截(长)图功能
前端·edge
Smartdaili China1 小时前
如何在 Microsoft Edge 中设置代理: 快速而简单的方法
前端·爬虫·安全·microsoft·edge·社交·动态住宅代理
秦老师Q1 小时前
「Chromeg谷歌浏览器/Edge浏览器」篡改猴Tempermongkey插件的安装与使用
前端·chrome·edge
滴水可藏海1 小时前
Chrome离线安装包下载
前端·chrome