弄了一整天,双token的思路总结,access_token,refresh_token

查了些资料,大概实现是拦截验证失败401,然后重放上次请求;

这是不太讲究的做法;原因:1、控制台401红色,不好看;2、业务逻辑千奇百怪,重放请求极大可能遇到各种问题。

目前实现方式如下:

1、请求发出前拦截

2、判断access_token是否过期

3、如果过期临时使用refresh_token访问,并异步更新access_token

下一次访问的时候,如果新的access_token已异步完成,使用新的access_token,没有完成还是用refresh_token访问;

稍有瑕疵是access_token异步未完成(授权时间未更新),refresh_token可能会多使用几次;(如果使用同步,感觉不如异步好)

extjs实现代码如下;

function asyncUpdateToken() {
return new Promise((resolve) => {
const { refresh_token } = localStore.getObject('user') || {};
fetch('/auth/refreshToken', {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
'authorization': `Bearer ${refresh_token}`,
},
}).then((response) => {
if (response.ok) {
resolve(response.text());
}
});
});
}

const checkTokenState = async function() {
const user = localStore.getObject('user') || {};
const { access_token } = user;
if (!access_token || tokenIsExp(access_token)) {
const newToken = await asyncUpdateToken();
const updatedUser = { ...user, access_token: newToken };
localStore.setObject('user', updatedUser);
if (newToken) {
const expTime = cmd.parseJwt(newToken)?.payload?.exp;
if (expTime) {
const expDate = new Date(expTime * 1000);
const formattedDate = Ext.Date.format(expDate, 'Y-m-d H:i:s');
console.log(`%c更新存储 ${formattedDate}`, 'color: #43bb88;font-size: 14px;font-weight: bold;');
}
}
}
};


const tokenIsExp = function(token) {
const { payload } = cmd.parseJwt(token);
return payload.exp * 1000 < Date.now(); // 已过期
};

function tokenExpToTime(token){
const { payload } = cmd.parseJwt(token);
let expDate = new Date(payload.exp * 1000);
return Ext.Date.format(expDate, 'Y-m-d H:i:s');
}

Ext.Ajax.addListener('beforerequest', function(conn, options, eOpts) {
if(1>2){
//这里无需token的请求跳过
return true
}
const { access_token, refresh_token } = localStore.getObject('user') || {};
console.log('START-----------------------------------')
console.log(`旧的存储 ${tokenExpToTime(access_token)} # ${tokenExpToTime(refresh_token)}`)
console.log(`当前时间 ${Ext.Date.format(new Date(), 'Y-m-d H:i:s')} @${tokenIsExp(access_token)
? '%c过期'
: '%c有效'}`,
`color: ${tokenIsExp(access_token)?"red":"green"} ;`)
void checkTokenState();
console.log(`xhr携带${options.url} exp${ tokenExpToTime(tokenIsExp(access_token) ? refresh_token : access_token) }`)
Ext.Ajax.setDefaultHeaders({
'authorization': `Bearer ${tokenIsExp(access_token) ? refresh_token : access_token}`
});
console.log('END-----------------------------------')
});


另外双token意义不是很大;
1、双token有权限access_token网络传输的次数多,要缩短有效时间被窃取后用不了多久。
2、refresh_token 建议不采用跟access_token完全一样的权限功能,绕弯一下加解密什么的。(refresh_token 仅是个完成刷新access_token功能的作用,其实现可以天马行空
3、服务器端没啥事做,请求刷新access_token的时候,处理refresh_token 返回个access_token就行。
parseJwt(token) {
const parts = token.split('.'); // JWT3个部分组成,使用“.”分割
if (parts.length !== 3) {
throw new Error('Invalid token format');
}
// 解码 Header PayloadBase64Url -> Base64 -> decodeURIComponent -> decode
const base64Url = parts[0]; // Header
const base64UrlPayload = parts[1]; // Payload
// 解码 Header
const header = JSON.parse(atob(base64Url.replace('-', '+').replace('_', '/')));
// 解码 Payload
const payload = JSON.parse(atob(base64UrlPayload.replace('-', '+').replace('_', '/')));
return {
header: header,
payload: payload,
};
}





posted on 2025-03-31 02:49  xsSystem  阅读(32)  评论(0)    收藏  举报