DeployHelper/internal/util/jwt_utils.go

158 lines
3.7 KiB
Go
Raw Normal View History

2025-08-01 16:38:08 +08:00
package util
import (
"ego/pkg/logger"
"fmt"
"os"
"sync"
"time"
"github.com/golang-jwt/jwt/v4"
"go.uber.org/zap"
)
const (
TokenExpireTime = 24 * time.Hour // Token 过期时间24小时
// TokenGroup token
TokenGroup = "user_token:"
)
var (
// JwtKey JWT签名密钥
JwtKey []byte
jwtKeyOnce sync.Once
)
// Claims JWT声明结构
type Claims struct {
ID string `json:"id"`
Username string `json:"username"`
jwt.RegisteredClaims
}
// initJwtKey 懒加载方式初始化JWT密钥
func initJwtKey() {
jwtKeyOnce.Do(func() {
secret := os.Getenv("JWT_SECRET")
if secret == "" {
secret = "default-jwt-secret-please-change-in-production"
// 注意这里不能使用logger因为可能还没初始化
// 如果需要日志,在配置初始化完成后再输出
}
JwtKey = []byte(secret)
})
}
// getJwtKey 获取JWT密钥确保已初始化
func getJwtKey() []byte {
initJwtKey()
return JwtKey
}
// GenerateToken 生成JWT Token
func GenerateToken(id, username string) (string, error) {
if id == "" || username == "" {
return "", fmt.Errorf("用户ID和用户名不能为空")
}
key := getJwtKey()
expireTime := time.Now().Add(TokenExpireTime)
claims := &Claims{
ID: id,
Username: username,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expireTime),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
Issuer: "ego-system",
Subject: username,
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(key)
if err != nil {
// 安全地记录错误如果logger已初始化
if logger.Logger != nil {
logger.Error(nil, "JWT Token生成失败", zap.Error(err))
}
return "", fmt.Errorf("token生成失败: %w", err)
}
return tokenString, nil
}
// ParseToken 解析JWT Token
func ParseToken(tokenString string) (jwt.MapClaims, error) {
if tokenString == "" {
return nil, fmt.Errorf("token不能为空")
}
key := getJwtKey()
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("无效的签名方法: %v", token.Header["alg"])
}
return key, nil
})
if err != nil {
// 安全地记录错误如果logger已初始化
if logger.Logger != nil {
logger.Error(nil, "JWT Token解析失败", zap.Error(err))
}
return nil, fmt.Errorf("token解析失败: %w", err)
}
if !token.Valid {
return nil, fmt.Errorf("token无效或已过期")
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return nil, fmt.Errorf("token claims格式错误")
}
return claims, nil
}
// ValidateToken 验证Token有效性
func ValidateToken(tokenString string) (*Claims, error) {
if tokenString == "" {
return nil, fmt.Errorf("token不能为空")
}
key := getJwtKey()
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (any, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("无效的签名方法: %v", token.Header["alg"])
}
return key, nil
})
if err != nil {
// 安全地记录错误如果logger已初始化
if logger.Logger != nil {
logger.Error(nil, "JWT Token验证失败", zap.Error(err))
}
return nil, fmt.Errorf("token验证失败: %w", err)
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, fmt.Errorf("token无效")
}
// LogJwtKeyStatus 在配置初始化完成后输出JWT密钥状态日志
func LogJwtKeyStatus() {
secret := os.Getenv("JWT_SECRET")
if secret == "" {
logger.Warn(nil, "使用默认JWT密钥生产环境请设置JWT_SECRET环境变量")
} else {
logger.Info(nil, "JWT密钥已从环境变量加载")
}
}