package token import ( "crypto/sha256" "encoding/hex" "errors" "time" "github.com/golang-jwt/jwt/v5" ) var ( jwtSecret []byte accessExpireH = 7 * 24 // 默认 7 天 refreshExpireH = 30 * 24 // 默认 30 天 ) // Init 初始化 JWT 密钥与有效期(main 启动时从配置加载) func Init(secret string, accessH, refreshH int) { jwtSecret = []byte(secret) if accessH > 0 { accessExpireH = accessH } if refreshH > 0 { refreshExpireH = refreshH } } // Claims access token 载荷 type Claims struct { UserID int64 `json:"user_id"` Username string `json:"username"` Roles []string `json:"roles"` jwt.RegisteredClaims } // RefreshClaims refresh token 载荷 type RefreshClaims struct { UserID int64 `json:"user_id"` jwt.RegisteredClaims } // GenerateToken 生成 access + refresh token func GenerateToken(userID int64, username string, roles []string) (string, string, error) { now := time.Now() claims := Claims{ UserID: userID, Username: username, Roles: roles, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(now.Add(time.Duration(accessExpireH) * time.Hour)), IssuedAt: jwt.NewNumericDate(now), NotBefore: jwt.NewNumericDate(now), }, } access, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(jwtSecret) if err != nil { return "", "", err } refreshClaims := RefreshClaims{ UserID: userID, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(now.Add(time.Duration(refreshExpireH) * time.Hour)), IssuedAt: jwt.NewNumericDate(now), NotBefore: jwt.NewNumericDate(now), }, } refresh, err := jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims).SignedString(jwtSecret) if err != nil { return "", "", err } return access, refresh, nil } // ParseToken 解析并校验 access token func ParseToken(tokenString string) (*Claims, error) { token, err := jwt.ParseWithClaims(tokenString, &Claims{}, keyFunc) if err != nil { return nil, err } if claims, ok := token.Claims.(*Claims); ok && token.Valid { return claims, nil } return nil, errors.New("invalid token") } // ParseRefreshToken 解析并校验 refresh token,返回用户 ID func ParseRefreshToken(tokenString string) (int64, error) { token, err := jwt.ParseWithClaims(tokenString, &RefreshClaims{}, keyFunc) if err != nil { return 0, err } if claims, ok := token.Claims.(*RefreshClaims); ok && token.Valid { return claims.UserID, nil } return 0, errors.New("invalid refresh token") } func keyFunc(t *jwt.Token) (any, error) { if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, errors.New("unexpected signing method") } return jwtSecret, nil } // GenerateShortID 生成 token 的短摘要,作为 Redis 存储 key 的一部分 func GenerateShortID(tokenString string) string { hash := sha256.Sum256([]byte(tokenString)) return hex.EncodeToString(hash[:8]) } // AccessExpireSeconds 返回 access token 有效期(秒),用于 Redis TTL func AccessExpireSeconds() uint { return uint(accessExpireH * 3600) } // RefreshExpireSeconds 返回 refresh token 有效期(秒),用于 Redis TTL func RefreshExpireSeconds() uint { return uint(refreshExpireH * 3600) }