package auth import ( "errors" "time" "perms-system-server/internal/consts" "perms-system-server/internal/middleware" "github.com/golang-jwt/jwt/v4" ) var ErrTokenTypeMismatch = errors.New("token type mismatch") type RefreshClaims struct { TokenType string `json:"tokenType"` UserId int64 `json:"userId"` ProductCode string `json:"productCode"` TokenVersion int64 `json:"tokenVersion"` jwt.RegisteredClaims } func GenerateAccessToken(secret string, expireSeconds int64, userId int64, username, productCode, memberType string, tokenVersion int64) (string, error) { now := time.Now() claims := middleware.Claims{ TokenType: consts.TokenTypeAccess, UserId: userId, Username: username, ProductCode: productCode, MemberType: memberType, TokenVersion: tokenVersion, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(now.Add(time.Duration(expireSeconds) * time.Second)), IssuedAt: jwt.NewNumericDate(now), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(secret)) } func GenerateRefreshToken(secret string, expireSeconds int64, userId int64, productCode string, tokenVersion int64) (string, error) { now := time.Now() claims := RefreshClaims{ TokenType: consts.TokenTypeRefresh, UserId: userId, ProductCode: productCode, TokenVersion: tokenVersion, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(now.Add(time.Duration(expireSeconds) * time.Second)), IssuedAt: jwt.NewNumericDate(now), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(secret)) } // GenerateRefreshTokenWithExpiry 签发 refreshToken,使用绝对过期时间(用于 token 轮转场景)。 func GenerateRefreshTokenWithExpiry(secret string, expiresAt time.Time, userId int64, productCode string, tokenVersion int64) (string, error) { now := time.Now() if !expiresAt.After(now) { return "", errors.New("refresh token has expired") } claims := RefreshClaims{ TokenType: consts.TokenTypeRefresh, UserId: userId, ProductCode: productCode, TokenVersion: tokenVersion, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(expiresAt), IssuedAt: jwt.NewNumericDate(now), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(secret)) } func ParseRefreshToken(tokenStr, secret string) (*RefreshClaims, error) { token, err := jwt.ParseWithClaims(tokenStr, &RefreshClaims{}, func(token *jwt.Token) (interface{}, error) { return []byte(secret), nil }) if err != nil { return nil, err } claims, ok := token.Claims.(*RefreshClaims) if !ok || !token.Valid { return nil, jwt.ErrSignatureInvalid } if claims.TokenType != consts.TokenTypeRefresh { return nil, ErrTokenTypeMismatch } return claims, nil }