package jwt import ( "errors" "time" "github.com/golang-jwt/jwt/v5" ) var ( ErrTokenExpired = errors.New("token已过期") ErrTokenNotValidYet = errors.New("token尚未激活") ErrTokenMalformed = errors.New("token格式错误") ErrTokenInvalid = errors.New("无效的token") ) // CustomClaims 自定义Claims type CustomClaims struct { BaseClaims BufferTime int64 `json:"bufferTime"` jwt.RegisteredClaims } // BaseClaims 基础Claims type BaseClaims struct { UUID string `json:"uuid"` ID uint `json:"userId"` Username string `json:"username"` NickName string `json:"nickName"` AuthorityID uint `json:"authorityId"` } // JWT JWT工具 type JWT struct { SigningKey []byte Issuer string ExpiresAt time.Duration BufferTime time.Duration } // NewJWT 创建JWT实例 func NewJWT(signingKey, issuer string, expiresAt, bufferTime time.Duration) *JWT { return &JWT{ SigningKey: []byte(signingKey), Issuer: issuer, ExpiresAt: expiresAt, BufferTime: bufferTime, } } // CreateToken 创建token func (j *JWT) CreateToken(baseClaims BaseClaims) (string, error) { claims := CustomClaims{ BaseClaims: baseClaims, BufferTime: int64(j.BufferTime / time.Second), RegisteredClaims: jwt.RegisteredClaims{ Issuer: j.Issuer, ExpiresAt: jwt.NewNumericDate(time.Now().Add(j.ExpiresAt)), NotBefore: jwt.NewNumericDate(time.Now().Add(-1000)), IssuedAt: jwt.NewNumericDate(time.Now()), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(j.SigningKey) } // CreateClaims 创建Claims func (j *JWT) CreateClaims(baseClaims BaseClaims) CustomClaims { return CustomClaims{ BaseClaims: baseClaims, BufferTime: int64(j.BufferTime / time.Second), RegisteredClaims: jwt.RegisteredClaims{ Issuer: j.Issuer, ExpiresAt: jwt.NewNumericDate(time.Now().Add(j.ExpiresAt)), NotBefore: jwt.NewNumericDate(time.Now().Add(-1000)), IssuedAt: jwt.NewNumericDate(time.Now()), }, } } // ParseToken 解析token func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) { token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) { return j.SigningKey, nil }) if err != nil { switch { case errors.Is(err, jwt.ErrTokenExpired): return nil, ErrTokenExpired case errors.Is(err, jwt.ErrTokenMalformed): return nil, ErrTokenMalformed case errors.Is(err, jwt.ErrTokenNotValidYet): return nil, ErrTokenNotValidYet default: return nil, ErrTokenInvalid } } if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { return claims, nil } return nil, ErrTokenInvalid } // NeedRefresh 判断是否需要刷新token func (j *JWT) NeedRefresh(claims *CustomClaims) bool { return claims.ExpiresAt.Unix()-time.Now().Unix() < claims.BufferTime } // CreateTokenByOldToken 根据旧token创建新token func (j *JWT) CreateTokenByOldToken(oldToken string, oldClaims CustomClaims) (string, *CustomClaims, error) { // 更新过期时间 oldClaims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(j.ExpiresAt)) oldClaims.IssuedAt = jwt.NewNumericDate(time.Now()) oldClaims.NotBefore = jwt.NewNumericDate(time.Now().Add(-1000)) token := jwt.NewWithClaims(jwt.SigningMethodHS256, oldClaims) newToken, err := token.SignedString(j.SigningKey) if err != nil { return "", nil, err } return newToken, &oldClaims, nil } // NewJWTFromConfig 从配置创建JWT实例(内部使用) func NewJWTFromConfig(signingKey, issuer, expiresTime, bufferTime string) *JWT { expires, _ := time.ParseDuration(expiresTime) if expires == 0 { expires = 7 * 24 * time.Hour // 默认7天 } buffer, _ := time.ParseDuration(bufferTime) if buffer == 0 { buffer = 24 * time.Hour // 默认1天 } return NewJWT(signingKey, issuer, expires, buffer) }