package jwt import ( "errors" "time" "github.com/golang-jwt/jwt/v5" ) // Claims JWT声明 type Claims struct { UserID uint `json:"user_id"` TenantID uint `json:"tenant_id"` Role string `json:"role"` jwt.RegisteredClaims } // JWTManager JWT管理器 type JWTManager struct { secretKey []byte } // NewJWTManager 创建JWT管理器 func NewJWTManager(secretKey string) *JWTManager { return &JWTManager{ secretKey: []byte(secretKey), } } // GenerateAccessToken 生成访问令牌 func (m *JWTManager) GenerateAccessToken(userID, tenantID uint, role string) (string, time.Time, error) { expiresAt := time.Now().Add(24 * time.Hour) // 24小时有效 claims := &Claims{ UserID: userID, TenantID: tenantID, Role: role, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(expiresAt), IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: "smart-customer-service", Subject: "access_token", }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString(m.secretKey) if err != nil { return "", time.Time{}, err } return tokenString, expiresAt, nil } // GenerateRefreshToken 生成刷新令牌 func (m *JWTManager) GenerateRefreshToken(userID, tenantID uint) (string, time.Time, error) { expiresAt := time.Now().Add(7 * 24 * time.Hour) // 7天有效 claims := &Claims{ UserID: userID, TenantID: tenantID, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(expiresAt), IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: "smart-customer-service", Subject: "refresh_token", }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString(m.secretKey) if err != nil { return "", time.Time{}, err } return tokenString, expiresAt, nil } // ValidateToken 验证令牌 func (m *JWTManager) ValidateToken(tokenString string) (*Claims, error) { token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) { // 验证签名方法 if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, errors.New("无效的签名方法") } return m.secretKey, nil }) if err != nil { return nil, err } if claims, ok := token.Claims.(*Claims); ok && token.Valid { return claims, nil } return nil, errors.New("无效的令牌") } // ParseToken 解析令牌(不验证过期) func (m *JWTManager) ParseToken(tokenString string) (*Claims, error) { parser := jwt.NewParser(jwt.WithoutClaimsValidation()) token, _, err := parser.ParseUnverified(tokenString, &Claims{}) if err != nil { return nil, err } if claims, ok := token.Claims.(*Claims); ok { return claims, nil } return nil, errors.New("无法解析令牌声明") } // GetTokenExpiration 获取令牌过期时间 func (m *JWTManager) GetTokenExpiration(tokenString string) (time.Time, error) { claims, err := m.ParseToken(tokenString) if err != nil { return time.Time{}, err } return claims.ExpiresAt.Time, nil } // IsTokenExpired 检查令牌是否过期 func (m *JWTManager) IsTokenExpired(tokenString string) (bool, error) { expiresAt, err := m.GetTokenExpiration(tokenString) if err != nil { return true, err } return time.Now().After(expiresAt), nil } // GeneratePasswordResetToken 生成密码重置令牌 func (m *JWTManager) GeneratePasswordResetToken(userID, tenantID uint) (string, time.Time, error) { expiresAt := time.Now().Add(1 * time.Hour) // 1小时有效 claims := &Claims{ UserID: userID, TenantID: tenantID, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(expiresAt), IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: "smart-customer-service", Subject: "password_reset", }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString(m.secretKey) if err != nil { return "", time.Time{}, err } return tokenString, expiresAt, nil } // GenerateEmailVerificationToken 生成邮箱验证令牌 func (m *JWTManager) GenerateEmailVerificationToken(userID, tenantID uint) (string, time.Time, error) { expiresAt := time.Now().Add(24 * time.Hour) // 24小时有效 claims := &Claims{ UserID: userID, TenantID: tenantID, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(expiresAt), IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: "smart-customer-service", Subject: "email_verification", }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString(m.secretKey) if err != nil { return "", time.Time{}, err } return tokenString, expiresAt, nil } // GenerateAPIToken 生成API令牌 func (m *JWTManager) GenerateAPIToken(tenantID uint, permissions []string) (string, time.Time, error) { expiresAt := time.Now().Add(30 * 24 * time.Hour) // 30天有效 claims := &Claims{ TenantID: tenantID, Role: "api", RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(expiresAt), IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: "smart-customer-service", Subject: "api_token", }, } // 添加自定义声明 token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) // 添加权限声明 token.Claims.(jwt.MapClaims)["permissions"] = permissions tokenString, err := token.SignedString(m.secretKey) if err != nil { return "", time.Time{}, err } return tokenString, expiresAt, nil } // GetTokenClaims 获取令牌声明(安全版本) func (m *JWTManager) GetTokenClaims(tokenString string) (map[string]interface{}, error) { claims, err := m.ValidateToken(tokenString) if err != nil { return nil, err } return map[string]interface{}{ "user_id": claims.UserID, "tenant_id": claims.TenantID, "role": claims.Role, "exp": claims.ExpiresAt.Time.Unix(), "iat": claims.IssuedAt.Time.Unix(), "iss": claims.Issuer, "sub": claims.Subject, }, nil } // RenewToken 续期令牌 func (m *JWTManager) RenewToken(tokenString string) (string, time.Time, error) { claims, err := m.ValidateToken(tokenString) if err != nil { return "", time.Time{}, err } // 根据令牌类型续期 switch claims.Subject { case "access_token": return m.GenerateAccessToken(claims.UserID, claims.TenantID, claims.Role) case "refresh_token": return m.GenerateRefreshToken(claims.UserID, claims.TenantID) case "api_token": // API令牌不自动续期 return "", time.Time{}, errors.New("API令牌不支持自动续期") default: return "", time.Time{}, errors.New("未知的令牌类型") } }