上一节 * 【图文并茂】ant design pro 如何对接后端个人信息接口
json web token
我们知道 token 就像身份证一样,但是它是有个过期时间的
比如说设为一个小时,
就像我们去访问微信支付那样,它们的 token 就是一个小时。
那我们的系统,你现在登录之后,你设了一个小时,过期时间,那它就在你登录之后的一个小时内一定会过期
无论你在中间有没有做访问
refresh token
问题出在这里,你中间一小时内,一直在操作,该过期还得过期,过期了,就要重新登录,未免体验太差了
我要的是,比如在一个如果中间有操作,你就帮我续下期。
比如说的这个中间,是七天内,只要七天内,当然你自己定,一天内也行,一天内只要有操作,你就一直帮我续一个小时。
这个时间是自己定的。
这里就涉及到两个时间。
JWT_EXPIRE=1d
REFRESH_JWT_EXPIRE=7d
比如这里有两个过期时间。
先看最后一个。
比如我现在登录了,那七天后会过期,我明天还有在操作,那就是七天后之后的过一天会过期
意思是,你只要在 REFRESH_JWT_EXPIRE 有效期内,有操作,那就一直延后七天,一直续。
也就是说这个时候会生成新的
REFRESH_JWT_SECRET
两个时间对应两个 token 的
JWT_SECRET
REFRESH_JWT_SECRET 这个是来续期用的
JWT_SECRET 这个应该叫 access_token ,来访问 api 用的,来验证的
所以 JWT_SECRET 这个可以设短一些,比如 一个小时。
一个小时后就过期了,别人拿到旧的也没用。
后端实现
先要实现 refresh Token 的 api
const refreshToken = handleAsync(async (req: Request, res: Response) => {
try {
const { refreshToken } = req.body;
if (!refreshToken) {
res.status(401);
throw new Error('You are not authenticated!');
}
const decoded = jwt.verify(refreshToken, process.env.REFRESH_JWT_SECRET as string) as DecodedToken;
const newRefreshToken = generateRefreshToken(decoded.id);
res.json({
success: true,
token: generateToken(decoded.id),
refreshToken: newRefreshToken,
});
} catch (err) {
console.error(err);
res.status(401);
throw new Error(err.message || 'Not authorized, token failed');
}
});
这里很简单,用旧的 refresh token 换新的,时间重新计算,继续延后七天,生成新的 access token 和 refresh token
新的 access token 可以让应用继续访问,
新的 refresh token 可以让租期延后。
所以 refresh token 从来不是解决安全问题的,只是解决体验问题。
const decoded = jwt.verify(refreshToken, process.env.REFRESH_JWT_SECRET as string) as DecodedToken;
这里总会验证老的 refresh. token 是否有效的。
如果无效,也就没后面啥事了。
前端实现
之前这里 【图文并茂】ant design pro 如何给后端发送 json web token - 请求拦截器的使用
要继续改一下。
requestInterceptors: [
async (config: RequestOptions) => {
// 拦截请求配置,进行个性化处理。
const url = config?.url;
const accessToken = localStorage.getItem('token');
if (url?.includes('/auth/refresh') || url?.includes('/auth/login')) {
return {
...config,
headers: { Authorization: `Bearer ${accessToken}` },
url,
};
}
let newToken;
// refresh token
if (accessToken) {
const currentDate = new Date();
const decodedToken: DecodedToken = jwtDecode<DecodedToken>(accessToken);
// expired
if (decodedToken.exp * 1000 < currentDate.getTime()) {
// send refresh token
const response = await refreshToken({
refreshToken: localStorage.getItem('refreshToken')!,
});
if (response.success) {
localStorage.setItem('token', response.token);
localStorage.setItem('refreshToken', response.refreshToken);
newToken = response.token;
}
}
}
return {
...config,
headers: { Authorization: `Bearer ${newToken ?? accessToken}` },
url,
};
},
],
那个请求请求拦截器部分要这样做
if (url?.includes('/auth/refresh') || url?.includes('/auth/login')) {
return {
...config,
headers: { Authorization: `Bearer ${accessToken}` },
url,
};
}
这里只是过滤掉不必要的 api
接下来
if (decodedToken.exp * 1000 < currentDate.getTime()) {
就去判断 access token 是否过期,过期才要续
只要 refresh token 不过期,总能续上新的
headers: { Authorization: Bearer ${newToken ?? accessToken}
},
后面这里只是用续上的来代替旧的。
到此完成。