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
|
|||
|
|
}
|
|||
|
|
|