注:本文为学习时记录的笔记,内容尚浅,后续有时间可能会完善
jwt-go框架学习记录
1.安装与配置
安装jwt-go库(5.0版本)
go get -u github.com/golang-jwt/jwt/v5
2.基本使用
1.生成jwt令牌,NewWithClaims
自定义claim,可在其中加入各种信息
// CustomClaims 自定义 Claims
type CustomClaims struct {
jwt.RegisteredClaims //导入官方默认的claims
//也可在这定义一些其他信息,如用户名称,用户id等
}
使用自定义的claim生成jwt令牌
//生成基本jwt令牌
token = jwt.NewWithClaims(
jwt.SigningMethodHS256, //签名方式
CustomClaims{
RegisteredClaims: jwt.RegisteredClaims{
//过期时间
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(global.App.Config.Jwt.JwtTtl) * time.*Second*)),
//这里我们把用户id存到默认claim的ID中
ID: user.GetUid(),
//jwt令牌发布者名称(防止不同应用发布的令牌混用)
Issuer: GuardName,
//生效时间
NotBefore: jwt.NewNumericDate(time.Now().Add(-1 * time.*Second*)),
},
},
)
//使用自定义的密钥转换为字符串,解析时需要用到该密钥,其中tokenStr就是返回给客户端的令牌
tokenStr, err := token.SignedString([]byte(global.App.Config.Jwt.Secret))
2.解析jwt令牌,ParseWithClaims
解析字符串令牌
// Token 解析校验
token, err := jwt.ParseWithClaims(tokenStr, &service.CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
// 返回加密时的密钥
return []byte(global.App.Config.Jwt.Secret), nil
})
获取claim对象
claims := token.Claims.(*service.CustomClaims)
//然后就可以获取到claim中保存的对应信息啦
3.jwt令牌黑名单策略
由于jwt令牌在是存储在客户端,服务端只负责解析,故服务端无法主动注销jwt令牌,这里我们可以使用黑名单策略来解决注销问题,将列入黑名单的jwt令牌存入redis,每次校验jwt令牌时如果存在黑名单内,则校验不通过。
我们还添加了黑名单宽限时间,防止用户jwt令牌加入黑名单后,短时间内又使用该令牌发起请求导致1请求失败的问题
简单实现
// JoinBlackList token 加入黑名单
func (jwtService *jwtService) JoinBlackList(token *jwt.Token) (err error) {
nowUnix := time.Now().Unix()
timer := time.Duration(token.Claims.(*CustomClaims).ExpiresAt.Unix()-nowUnix) * time.Second
// 将 token 剩余时间设置为缓存有效期,并将当前时间作为缓存 value 值
err = global.App.Redis.SetNX(context.Background(), jwtService.getBlackListKey(token.Raw), nowUnix, timer).Err()
return
}
// IsInBlacklist token 是否在黑名单中
func (jwtService *jwtService) IsInBlacklist(tokenStr string) bool {
joinUnixStr, err := global.App.Redis.Get(context.Background(), jwtService.getBlackListKey(tokenStr)).Result()
joinUnix, err := strconv.ParseInt(joinUnixStr, 10, 64)
if joinUnixStr == "" || err != nil {
return false
}
// JwtBlacklistGracePeriod 为黑名单宽限时间,避免并发请求失效
if time.Now().Unix()-joinUnix < global.App.Config.Jwt.JwtBlacklistGracePeriod {
return false
}
return true
}
//每次在校验token时就需要调用IsInBlacklist方法判断是否在黑名单内,如果在,则该令牌已失效
4.jwt令牌续签问题
在令牌有效期内进行有效操作刷新令牌过期时间,对用户操作更加友好
简单实现
// token 续签
if claims.ExpiresAt.Time.Unix()-time.Now().Unix() < global.App.Config.Jwt.RefreshGracePeriod {
// 使用了锁机制来确保只有一个续签操作在运行
lock := global.Lock("refresh_token_lock", global.App.Config.Jwt.JwtBlacklistGracePeriod)
if lock.Get() {
err, user := service.JwtService.GetUserInfo(GuardName, claims.ID)
if err != nil {
global.App.Log.Error(err.Error())
lock.Release()
} else {
tokenData, _, _ := service.JwtService.CreateToken(GuardName, user)
c.Header("new-token", tokenData.AccessToken)
c.Header("new-expires-in", strconv.Itoa(tokenData.ExpiresIn))
_ = service.JwtService.JoinBlackList(token)
}
}
}