DeployHelper/internal/util/jwt_utils.go

158 lines
3.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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密钥已从环境变量加载")
}
}