从用户体验角度考虑,经常登录并不是很理想,一般的做法是设置一个jwt过期时间,比如一周,一周后就会强制过期。
这种方法非常生硬,比较理想的方法应该是超出一周没有任何操作才会过期,否则会自动续期。
1.实现方案
在网上找了很多方案,一类是把token放进redis里,如果有访问则刷新过期时间,如果过期在redis搜不到这个token则需要重新登录。
这类方案就违背了jwt无状态的优势,变得和cookie一样。
综合考虑下采用了第二种方案,在检测到token过期时查看是否已经超出一周,没有超过的话生成一个新的token返回给前端,这需要与前端约定一个code,接收到这个code时会先更新token,然后重新发起请求。
2.Go检测jwt是否过期
提取token时间,与现在时间进行比较,如果超出没有超出一周则生成新的token:
//utils/jwt.go
func ParseToken(tokenString string) (*Claims, error) {
claims = new(Claims)
token, err := jwt.ParseWithClaims(
tokenString,
claims,
func(token *jwt.Token) (i interface{}, err error) {
return jwtKey, nil
},
)
if err != nil {
if token != nil {
// 一周的秒数
week := 60 * 60 * 24 * 7
// 过期时间戳
expireTime := time.Now().Unix() - claims.ExpiresAt
// 如果过期时间小于一周,返回402
if int(expireTime) < week {
return claims, errors.New("402")
}
}
return nil, err
}
if token.Valid {
return claims, nil
}
return nil, errors.New("invalid Token")
}
2.1在中间件中进行检测
中间件在接收到402后使用返回的token信息生成一个新的token,然后返回给前端:
if userInfo, err := utils.ParseToken(token); err != nil {
if err.Error() == "402" {
tokenStr, _ := utils.CreateJWT(userInfo.UserInfo)
response.Warning(c, 402, "", gin.H{
"token": tokenStr,
})
c.Abort()
} else {
response.Warning(c, 403, "error", "token超时,请重新登录")
c.Abort()
}
}
3.前端处理新token
通常前端都会将axios进行封装,检测到约定好的更新token则会重新发一遍请求,这里约定了402:
// token过期,但是有返回
if (code === 402) {
setToken(res.data.data.token)
return axios.request(res.config)
}
res.config是当前调用请求的所有数据,重新发起即可。
「回顾2022,展望2023,我正在参与2022年终总结征文大赛活动」