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 { UserID uint `json:"userId"` Username string `json:"username"` AuthorityID uint `json:"authorityId"` BufferTime int64 `json:"bufferTime"` jwt.RegisteredClaims } // 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(userID uint, username string, authorityID uint) (string, error) { claims := CustomClaims{ UserID: userID, Username: username, AuthorityID: authorityID, 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) } // 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.Time.Sub(time.Now()) < j.BufferTime }