package jwt import ( "errors" "time" "github.com/golang-jwt/jwt/v5" ) var ( ErrInvalidToken = errors.New("invalid token") ErrExpiredToken = errors.New("token has expired") ) // Claims JWT 声明 type Claims struct { UserID string `json:"user_id"` Username string `json:"username"` jwt.RegisteredClaims } // JWT JWT工具 type JWT struct { secretKey string accessTokenExpire time.Duration refreshTokenExpire time.Duration } // New 创建JWT实例 func New(secret string, accessExpire, refreshExpire time.Duration) *JWT { return &JWT{ secretKey: secret, accessTokenExpire: accessExpire, refreshTokenExpire: refreshExpire, } } // GenerateAccessToken 生成访问令牌 func (j *JWT) GenerateAccessToken(userID, username string) (string, error) { now := time.Now() claims := Claims{ UserID: userID, Username: username, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(now.Add(j.accessTokenExpire)), IssuedAt: jwt.NewNumericDate(now), NotBefore: jwt.NewNumericDate(now), Issuer: "carrot_bbs", }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(j.secretKey)) } // GenerateRefreshToken 生成刷新令牌 func (j *JWT) GenerateRefreshToken(userID, username string) (string, error) { now := time.Now() claims := Claims{ UserID: userID, Username: username, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(now.Add(j.refreshTokenExpire)), IssuedAt: jwt.NewNumericDate(now), NotBefore: jwt.NewNumericDate(now), Issuer: "carrot_bbs", ID: "refresh", }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(j.secretKey)) } // ParseToken 解析令牌 func (j *JWT) ParseToken(tokenString string) (*Claims, error) { token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) { return []byte(j.secretKey), nil }) if err != nil { return nil, err } if claims, ok := token.Claims.(*Claims); ok && token.Valid { return claims, nil } return nil, ErrInvalidToken } // ValidateToken 验证令牌 func (j *JWT) ValidateToken(tokenString string) error { claims, err := j.ParseToken(tokenString) if err != nil { return err } // 检查是否是刷新令牌 if claims.ID == "refresh" { return errors.New("cannot use refresh token as access token") } return nil }