vue axios 刷新token 刷新jwt js刷新token http刷新token
前言 在互联网的登陆系统中一般有session cookie 和 jwt token来进行验证用户是否登录的.下面来说一下关于 jwt的坑:1.首先登录成功的返回,其中jwt的有效期为2小时,refreshJwt的有效期为30天如下:2.保存jwt 和refreshJwt在cookie中,当然这里不一定要保存在cookie中,也可以保存在别的地方.这里以cookie为例子:3....
前言 在互联网的登陆系统中一般有session cookie 和 jwt token来进行验证用户是否登录的.下面来说一下关于 jwt的坑:
1.首先登录成功的返回,其中jwt的有效期为2小时,refreshJwt的有效期为30天如下:
2.保存jwt 和refreshJwt 在cookie中,当然这里不一定要保存在cookie中,也可以保存在别的地方.这里以cookie为例子:
3.核心思路:当jwt过期时(超过了2小时),调用后端接口会返回401的状态码(这个时候代表jwt已经过期了,需要刷新了),再拦截器中拦截响应的数据,这个时候就需要拿refreshJwt去调用后端的刷新接口,把获取到的最新jwt覆盖掉本地的jwt和refreshJwt,然后再次进行刚才的请求,并且把获取到的数据,从响应拦截器中返回回去.
步骤大概如此: 前端发起ajax请求 => 后端发现jwt已经过期,返回401状态码 => 前端拦截响应数据,并发起刷新token的请求 => 拿到最新的jwt和refresh,保存到本地 => 拿到最新的jwt去进行刚刚未请求成功的接口 => 获取到刚刚请求的结果,覆盖第一次请求失败(状态码为401)的响应数据 => 返回第二次请求的结果.
另一种情况就是:前端发起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的工具.无需重点关注
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. 拦截器配置如下:
/***
* 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. 操作演示:
已经过期了,需要进行刷新操作
进行刷新覆盖操作
重新进行请求,并覆盖掉原来的
大功告成,小生的刷新token思路如此,如阁下有更好的思路,方便分享,请留言哦,刷新token已经可以了.有问题的小伙伴请求留言.
以前写了一篇发现在并发处理的时候有问题:
然后参考了一部分资料修改了代码,完美解决.
参考资料:
更多推荐
所有评论(0)