167 lines
4.0 KiB
Go
167 lines
4.0 KiB
Go
/**
|
||
* 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
|
||
}
|
||
|