JWT在微服务中的认证中经常使用,本文记录jwt搭配gin来给app的api接口提供基本的认证服务,首先介绍jwt的封装,然后介绍实现gin认证的中间件以提供jwt的认证,最后介绍一下token过期时,token的刷新过程。
这里需要注意一些问题,比如出于安全考虑,短token一般有时间限制在2小时,长token一般在15~30天不等。jwt也考虑在不同微服务间提供共享的认证服务所需要注意的问题。
1.封装golang-jwt的使用
1 | // JWT基本数据结构 |
2. 给gin提供jwt认证中间件
这里要注意如果是token过期,返回错误401,对应app拿长token去刷新短token1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38// JWTAuth 中间件,检查token
func JWTAuth() gin.HandlerFunc {
return func(c *gin.Context) {
result := &model.ResultInfo{Result: model.ESystemErr}
token := c.Request.Header.Get("token")
if token == "" {
result.Result = model.EInvalidToken
c.JSON(http.StatusOK, result)
c.Abort()
return
}
j := NewJWT()
// 解析token中包含的相关信息
claims, err := j.ParserToken(token)
if err != nil {
// token过期
if err == TokenExpired {
result.Result = model.EExpireTimeOut
c.JSON(http.StatusUnauthorized, result)
c.Abort()
log.Errorf("JWTAuth error,timeout token:%s", token)
return
}
// 其他错误
log.Errorf("JWTAuth error,token error:%s", token)
result.Result = model.ETokenErr
c.JSON(http.StatusOK, result)
c.Abort()
return
}
// 解析到具体的claims相关信息
c.Set("claims", claims)
//c.Set("uid", claims.Uuid)
}
}
在gin中增加JWTAuth1
2
3
4
5func initUserRouter(p_groups *gin.RouterGroup) {
groups := p_groups.Group("/user")
groups.Use(md.JWTAuth())
// ...
}
3. 生成token.
注意:首次登录时要么用户名密码登录,要么第三方认证登录之后,签发本地token,这个jwt的token是可以在后端进行校验的。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34// token生成器
func GenerateToken(info *ResultInfo, account *Account) error {
// 构造SignKey: 签名和解签名需要使用一个值
j := md.NewJWT()
now := time.Now().Add(model.TokenAliveTime)
// 构造用户claims信息(负荷)
claims := md.CustomClaims{
jwt.RegisteredClaims{
Issuer: "chessbooks.cn",
Subject: "chess",
ExpiresAt: jwt.NewNumericDate(now),
ID: strconv.Itoa(jwtGenteratorId),
},
account.MemberId,
account.Username,
}
// 根据claims生成token对象
token, err := j.CreateToken(claims)
if err != nil {
return err
}
rtoken, err := generateRefreshToken(account.MemberId)
if err != nil {
return err
}
info.Token = token
info.RefreshToken = rtoken
info.UUID = strconv.FormatUint(account.MemberId, 10)
return nil
}
jwt使用注意事项:
- 首次登录时,需要颁发短token和长token,一般认证需要用的是短token,短token如果泄漏,那么安全边界就是下一次刷新token时,用户因为没有长token而不能续签。这里说明的问题是jwt的token是无状态的,泄漏的token,后台不能撤销token,只能等待token的自动过期。所以部署微服务一般强制要求使用https。
- 短token过期时,使用长token去刷新短token。
- 长token过期时,要求用户重新登录。(有人说要自动续长token的时间,我在这里建议尽量不要开这个口子,长token30天期限重新登录,体验上也没有什么区别。)
- 登录不能代替认证,jwt的颁发是服务器自颁发和防篡改的,由于token有可能被劫持的关系,类似拿着你身份证的人不一定是你自己,所以登录认证和token并不是一一对应。