用Go-Gin实现JWT自动续期,全程无感

2,242 阅读2分钟

从用户体验角度考虑,经常登录并不是很理想,一般的做法是设置一个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年终总结征文大赛活动