Files

167 lines
4.0 KiB
Go
Raw Permalink Normal View History

2025-12-03 11:00:47 +08:00
/**
* package utils
*
* JWT Token生成和验证工具包
*
* 功能概述
* 1. 生成JWT Token包含用户ID和过期时间
* 2. 解析JWT Token验证签名和过期时间
* 3. 验证Token有效性提取用户ID
*
* 使用场景
* - 用户登录后生成Token
* - API请求时验证Token
* - 从Token中提取用户信息
*/
package utils
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/spf13/viper"
)
// jwtSecret JWT签名密钥从配置文件读取
var jwtSecret []byte
/**
* init
*
* 功能初始化JWT签名密钥
*
* 步骤
* 1. 从配置文件读取JWT密钥
* 2. 如果配置文件中没有使用默认密钥仅用于开发环境
* 3. 将密钥转换为字节数组存储
*
* 注意生产环境必须使用配置文件中的强随机密钥
*/
func init() {
secret := viper.GetString("jwt.secret")
if secret == "" {
secret = "xk-websocket-secret-key-2025" // 默认密钥,生产环境应使用配置
}
jwtSecret = []byte(secret)
}
/**
* Claims
*
* JWT Token的载荷结构
*
* 字段说明
* - UserID: 用户ID业务数据
* - RegisteredClaims: JWT标准声明过期时间签发时间等
*/
type Claims struct {
UserID string `json:"user_id"` // 用户ID
jwt.RegisteredClaims // JWT标准声明
}
/**
* GenerateToken
*
* 功能生成JWT Token
*
* 步骤
* 1. 设置Token过期时间默认7天
* 2. 创建Claims对象包含用户ID和标准声明
* 3. 使用HS256算法签名Token
* 4. 返回Token字符串
*
* @param userID 用户ID
* @returns token字符串和错误
*/
func GenerateToken(userID string) (string, error) {
// 步骤1: 设置Token过期时间7天后过期
expirationTime := time.Now().Add(7 * 24 * time.Hour)
// 步骤2: 创建Claims对象包含用户ID和标准声明
claims := &Claims{
UserID: userID, // 业务数据用户ID
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expirationTime), // 过期时间
IssuedAt: jwt.NewNumericDate(time.Now()), // 签发时间
NotBefore: jwt.NewNumericDate(time.Now()), // 生效时间(立即生效)
},
}
// 步骤3: 使用HS256算法创建Token并签名
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(jwtSecret)
if err != nil {
return "", err
}
// 步骤4: 返回Token字符串
return tokenString, nil
}
/**
* ParseToken
*
* 功能解析JWT Token
*
* 步骤
* 1. 创建空的Claims对象
* 2. 使用密钥解析Token并验证签名
* 3. 检查Token是否有效签名正确未过期
* 4. 返回Claims对象
*
* @param tokenString token字符串
* @returns Claims和错误
*/
func ParseToken(tokenString string) (*Claims, error) {
// 步骤1: 创建空的Claims对象
claims := &Claims{}
// 步骤2: 解析Token并验证签名
// 使用密钥验证Token的签名是否有效
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
return jwtSecret, nil // 返回签名密钥
})
if err != nil {
return nil, err // 解析失败(签名错误、格式错误等)
}
// 步骤3: 检查Token是否有效签名正确、未过期
if !token.Valid {
return nil, errors.New("invalid token")
}
// 步骤4: 返回解析后的Claims对象
return claims, nil
}
/**
* ValidateToken
*
* 功能验证Token有效性并提取用户ID
*
* 步骤
* 1. 调用ParseToken解析Token
* 2. 如果解析成功从Claims中提取用户ID
* 3. 返回用户ID
*
* 使用场景
* - 中间件中验证Token
* - API处理器中获取当前用户ID
*
* @param tokenString token字符串
* @returns 用户ID和错误
*/
func ValidateToken(tokenString string) (string, error) {
// 步骤1: 解析Token
claims, err := ParseToken(tokenString)
if err != nil {
return "", err // Token无效或已过期
}
// 步骤2: 从Claims中提取用户ID
return claims.UserID, nil
}