package auth import ( "crypto/rsa" "errors" "time" "github.com/golang-jwt/jwt/v5" ) // YggdrasilJWTService Yggdrasil JWT服务(使用RSA512) type YggdrasilJWTService struct { privateKey *rsa.PrivateKey publicKey *rsa.PublicKey issuer string } // NewYggdrasilJWTService 创建新的Yggdrasil JWT服务 func NewYggdrasilJWTService(privateKey *rsa.PrivateKey, issuer string) *YggdrasilJWTService { if issuer == "" { issuer = "carrotskin" } return &YggdrasilJWTService{ privateKey: privateKey, publicKey: &privateKey.PublicKey, issuer: issuer, } } // YggdrasilTokenClaims Yggdrasil Token声明 type YggdrasilTokenClaims struct { Version int `json:"version"` // 版本号,用于失效旧Token UserID int64 `json:"user_id"` // 用户ID ProfileID string `json:"profile_id,omitempty"` // 选中的Profile UUID jwt.RegisteredClaims } // StaleTokenPolicy Token过期策略 type StaleTokenPolicy int const ( StalePolicyAllow StaleTokenPolicy = iota // 允许过期的Token(但未过StaleAt) StalePolicyDeny // 拒绝过期的Token ) // GenerateAccessToken 生成AccessToken JWT func (j *YggdrasilJWTService) GenerateAccessToken( userID int64, clientUUID string, version int, profileID string, expiresAt time.Time, staleAt time.Time, ) (string, error) { claims := YggdrasilTokenClaims{ Version: version, UserID: userID, ProfileID: profileID, RegisteredClaims: jwt.RegisteredClaims{ Subject: clientUUID, IssuedAt: jwt.NewNumericDate(time.Now()), ExpiresAt: jwt.NewNumericDate(expiresAt), NotBefore: jwt.NewNumericDate(time.Now()), Issuer: j.issuer, }, } token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) return token.SignedString(j.privateKey) } // ParseAccessToken 解析AccessToken JWT func (j *YggdrasilJWTService) ParseAccessToken(accessToken string, stalePolicy StaleTokenPolicy) (*YggdrasilTokenClaims, error) { token, err := jwt.ParseWithClaims(accessToken, &YggdrasilTokenClaims{}, func(token *jwt.Token) (interface{}, error) { // 验证签名算法 if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok { return nil, errors.New("不支持的签名算法,需要使用RSA") } return j.publicKey, nil }) if err != nil { return nil, err } if !token.Valid { return nil, errors.New("无效的token") } claims, ok := token.Claims.(*YggdrasilTokenClaims) if !ok { return nil, errors.New("无法解析token声明") } // 检查StaleAt(如果设置了拒绝过期策略) if stalePolicy == StalePolicyDeny && claims.ExpiresAt != nil { if time.Now().After(claims.ExpiresAt.Time) { return nil, errors.New("token已过期") } } return claims, nil } // GetPublicKey 获取公钥 func (j *YggdrasilJWTService) GetPublicKey() *rsa.PublicKey { return j.publicKey }