一、前言
不知道小伙伴们有没有遇到过这么一种情况,你在网站上填一份表格的时候,填了大半天,最后点击提交按钮的时候,突然弹框提示你:"登录状态已过期,请重新登录!"。此时此刻的你,内心肯定很崩溃,其实这个就是Token过期处理不当,导致用户体验很差的问题。我们今天就来聊聊,无感刷新Token是如何实现的,让用户即使在Token过期的情况下,也能无缝地继续操作。
二、令牌设计与生成
首先我们需要两个Token:
accessToken: 这是我们每次请求业务接口时,都需要在header请求头里带上的令牌。它的特点是生命周期短(比如1小时、半小时),因为暴露的风险更高。refreshToken: 它的唯一作用,就是用来获取一个新的accessToken和refreshToken。它的特点是生命周期长(比如7天),并且需要被安全地存储。
整体的流程设计:
用户进行登录,登录接口会返回accessToken和refreshToken这两个Token,前端定时去检查accessToken是否有效,比如每5分钟去检查一次,判断到accessToken还剩5分钟就过期的时候,就调用刷新accessToken、refreshToken的接口。
下面附上部分示例代码:
/**
* 刷新令牌(该接口会返回新的accessToken和refreshToken)
*/
@GetMapping("/refresh")
public Tokens getRefreshToken() {
UserDO user = LocalUser.getLocalUser();
return jwt.generateTokens(user.getId());
}
public Tokens generateTokens(long identity) {
String access = this.generateToken("access", identity, this.accessExpire);
String refresh = this.generateToken("refresh", identity, this.refreshExpire);
return new Tokens(access, refresh);
}
public class Tokens {
private String accessToken;
private String refreshToken;
public Tokens(String accessToken, String refreshToken) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
public String getAccessToken() {
return this.accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getRefreshToken() {
return this.refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String toString() {
return "Tokens{accessToken='" + this.accessToken + '\'' + ", refreshToken='" + this.refreshToken + '\'' + '}';
}
}
前端代码:
// 定时检查Token有效期
setInterval(() => {
const token = localStorage.getItem('accessToken');
if (token && isTokenExpiringSoon(token)) { // 剩余<5分钟
axios.post('/auth/refresh', {}, { withCredentials: true })
.then(res => {
localStorage.setItem('accessToken', res.data.accessToken);
});
}
}, 300000); // 每5分钟检查