package security import ( "crypto/rand" "crypto/sha256" "encoding/base64" "encoding/hex" "errors" "time" "github.com/golang-jwt/jwt/v5" ) type AdminClaims struct { AdminID string `json:"admin_id"` Username string `json:"username"` TokenType string `json:"token_type"` jwt.RegisteredClaims } type TokenService struct { Secret []byte AccessTTL time.Duration RefreshTTL time.Duration } func (s TokenService) NewAccessToken(adminID, username string) (string, time.Time, error) { now := time.Now() expires := now.Add(s.AccessTTL) claims := AdminClaims{AdminID: adminID, Username: username, TokenType: "access", RegisteredClaims: jwt.RegisteredClaims{Subject: adminID, IssuedAt: jwt.NewNumericDate(now), ExpiresAt: jwt.NewNumericDate(expires)}} token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(s.Secret) return token, expires, err } func (s TokenService) ParseAccessToken(raw string) (*AdminClaims, error) { claims := new(AdminClaims) token, err := jwt.ParseWithClaims(raw, claims, func(token *jwt.Token) (any, error) { if token.Method != jwt.SigningMethodHS256 { return nil, errors.New("不支持的签名算法") } return s.Secret, nil }) if err != nil || !token.Valid || claims.TokenType != "access" { return nil, errors.New("登录凭证无效或已过期") } return claims, nil } func (s TokenService) NewRefreshToken() (plain, hash string, expires time.Time, err error) { data := make([]byte, 32) if _, err = rand.Read(data); err != nil { return } plain = base64.RawURLEncoding.EncodeToString(data) hash = HashToken(plain) expires = time.Now().Add(s.RefreshTTL) return } func HashToken(token string) string { sum := sha256.Sum256([]byte(token)) return hex.EncodeToString(sum[:]) }