前端|vue axios 刷新token 刷新jwt js刷新token http刷新token

前言 在互联网的登陆系统中一般有session cookie 和 jwt token来进行验证用户是否登录的.下面来说一下关于 jwt的坑:
1.首先登录成功的返回,其中jwt的有效期为2小时,refreshJwt的有效期为30天如下:
前端|vue axios 刷新token 刷新jwt js刷新token http刷新token
文章图片

2.保存jwt 和refreshJwt 在cookie中,当然这里不一定要保存在cookie中,也可以保存在别的地方.这里以cookie为例子:
前端|vue axios 刷新token 刷新jwt js刷新token http刷新token
文章图片
3.核心思路:当jwt过期时(超过了2小时),调用后端接口会返回401的状态码(这个时候代表jwt已经过期了,需要刷新了),再拦截器中拦截响应的数据,这个时候就需要拿refreshJwt去调用后端的刷新接口,把获取到的最新jwt覆盖掉本地的jwt和refreshJwt,然后再次进行刚才的请求,并且把获取到的数据,从响应拦截器中返回回去.
步骤大概如此: 前端发起ajax请求 => 后端发现jwt已经过期,返回401状态码 => 前端拦截响应数据,并发起刷新token的请求 => 拿到最新的jwt和refresh,保存到本地 => 拿到最新的jwt去进行刚刚未请求成功的接口 => 获取到刚刚请求的结果,覆盖第一次请求失败(状态码为401)的响应数据 => 返回第二次请求的结果.
【前端|vue axios 刷新token 刷新jwt js刷新token http刷新token】另一种情况就是:前端发起ajax请求 => 后端发现jwt已经过期,返回401状态码 => 前端拦截响应数据,并发起刷新token的请求 =>后端返回了410的状态码(这个时候代表refreshJwt也过期了,需要进行重新登录了) => 真正的过期了,需要跳转到登录界面.
这样刷新token是在用户无感的情况下进行操作的.
下面进行贴代码了:
此拦截器模块为http,封装的步骤参考:https://blog.csdn.net/qq_33270001/article/details/86612528注:原来的http模块中并未封装refreshJwt的操作.
3. 新建一个refreshToken.js的文件:
看核心操作就是,util.getCookie(`REFRESHJWT-COLLECTOR`); 这些都是封装来操作cookie的工具.无需重点关注
前端|vue axios 刷新token 刷新jwt js刷新token http刷新token
文章图片

import axios from 'axios'; import conf from '@config'; import util from '@util'; import { BASE_URL, API_SERVER_FILTER } from './models/types'; export default async() => { try { const refreshToken = util.getCookie(conf.REFRESH_AUTHTOKEN_STORE_KEY); const { data } = await axios.post(`${BASE_URL}${API_SERVER_FILTER}/user/reftoken`, { token: util.getCookie(conf.AUTHTOKEN_STORE_KEY), platform: 'PC', refToken: refreshToken }); const { code, data: { token, refToken } } = data || {}; if (token && refToken) { util.setCookie(conf.AUTHTOKEN_STORE_KEY, token); //jwt util.setCookie(conf.REFRESH_AUTHTOKEN_STORE_KEY, refToken); //刷新token } return { code, data: { token } }; } catch (error) { console.log(error); } };

4. 拦截器配置如下:
前端|vue axios 刷新token 刷新jwt js刷新token http刷新token
文章图片


/*** * Created by Simple on 2018/1/14 0014. * Http请求控制器模块 */import http from 'axios'; import { Loading, Message } from 'element-ui'; import router from '@/router'; import conf from '@config'; import util from '@util'; import refreshToken from './refreshToken'; const instance = http.create(); // instance 配置 instance.defaults.timeout = 1000 * 30; instance.defaults.baseURL = process.env.NODE_ENV === 'production' ? conf.productionUrl : conf.devUrl; instance.setToken = (token) => { instance.defaults.headers.Authorization = token; util.setCookie(conf.AUTHTOKEN_STORE_KEY, token); //jwt }// 配置通用请求动画 let loading = null; // 是否正在刷新的标记 let isRefreshing = false; // 重试队列,每一项将是一个待执行的函数形式 let requests = []; /** * 通用请求拦截配置 * @param {*} config */ const instanceConf = (config) => { //===========================签名 S===================================== const { noncestr, timestamp, sign } = util.getSign(config); config.headers.noncestr = noncestr; config.headers.timestamp = timestamp; config.headers.sign = sign; //===========================签名 E===================================== config.headers.platform = `PC`; //===========================移除重置空字符串 S===================================== util.resetParamsEmpty(config); //===========================移除重置空字符串 E===================================== config.headers.Authorization = util.getCookie(conf.AUTHTOKEN_STORE_KEY); if (config.url.indexOf('/user/gqrcstatus') == -1) { loading = Loading.service({ lock: true, text: '拼命加载中...', background: 'rgba(255, 255, 255, .8)', }); } return config; }instance.interceptors.request.use(instanceConf, err => { if (loading && err) loading.close(); return Promise.reject(err); }); // http response 拦截器 instance.interceptors.response.use(async (response) => { let data = {}; if (response && response.data) { let code = Number(response.code || response.data.code) data = response.data; if (code === 200) { data = response.data; }else if(code == 402){ if(!isRefreshing){ isRefreshing = true; try { const { data: { token } } = await refreshToken(); if(token){ instance.setToken(token); response.config.headers.Authorization = token; // 已经刷新了token,将所有队列中的请求进行重试 requests.forEach(cb => cb(token)); requests = []; return instance(instanceConf(response.config)); } } catch (error) {//刷新时候直接判断token 不用判断code console.error('refreshtoken error =>', error); routerRedirect({ redirect: router.currentRoute.fullPath }); } finally { isRefreshing = false; } }else{ // 正在刷新token,将返回一个未执行resolve的promise return new Promise((resolve) => { // 将resolve放进队列,用一个函数形式来保存,等token刷新后直接执行 requests.push((token) => { response.config.headers.Authorization = token; resolve(instance(instanceConf(response.config))); }); }); } }else if (code === 401) { routerRedirect({ redirect: router.currentRoute.fullPath }); } else if(![30073,30072,30074,30078,30075,30076].includes(code)) { Message.error(response.data && response.data.message || '网络连接出错!请稍后刷新重试!'); } } if (loading) loading.close(); return data; }, (error) => { console.log(`object`, error); if (loading) loading.close(); Message.error('哎呀~ (?﹏?)网络又开小差了,请稍后刷新重试!'); return Promise.reject(error.response.data); }); /** * 重定向 */ const routerRedirect = ({ path = '/login', redirect })=>{ Message.warning(`身份过期,请重新登录!`); if (router.currentRoute.path != '/login') { setTimeout(() => { router.replace({ path, query: { redirect } }); }, 1200); } }export default instance;

5.操作演示:
已经过期了,需要进行刷新操作
前端|vue axios 刷新token 刷新jwt js刷新token http刷新token
文章图片

进行刷新覆盖操作
前端|vue axios 刷新token 刷新jwt js刷新token http刷新token
文章图片

重新进行请求,并覆盖掉原来的
前端|vue axios 刷新token 刷新jwt js刷新token http刷新token
文章图片

大功告成,小生的刷新token思路如此,如阁下有更好的思路,方便分享,请留言哦,刷新token已经可以了.有问题的小伙伴请求留言.
以前写了一篇发现在并发处理的时候有问题:
然后参考了一部分资料修改了代码,完美解决.
参考资料:
https://segmentfault.com/a/1190000020986592

    推荐阅读