You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
dark 4f1aa63b1c api 1 week ago
..
README.md api 1 week ago
bcrypt.go api 1 week ago
bcrypt_test.go api 1 week ago
jwt.go api 1 week ago
jwt_test.go api 1 week ago

README.md

JWT 工具使用说明

这是一个基于 Go-Zero 框架的 JWT 工具,支持 token 的生成、验证、刷新和管理功能,并将 token 存储在 Redis 中。

功能特性

  • JWT token 生成和验证
  • Redis 存储管理
  • Token 刷新机制
  • 用户多 token 管理
  • 批量 token 操作
  • 完整的错误处理
  • 性能优化
  • 完整的测试覆盖

依赖说明

# 主要依赖
go get github.com/golang-jwt/jwt/v5
go get github.com/redis/go-redis/v9

# 测试依赖
go get github.com/stretchr/testify

配置说明

1. 配置文件 (usercenter.yaml)

Auth:
  AccessSecret: "your-secret-key"
  AccessExpire: 604800 # 7天过期时间(秒)

2. 配置结构体 (config.go)

type Auth struct {
    AccessSecret string // JWT 密钥
    AccessExpire int64  // JWT 过期时间(秒)
}

基本使用方法

1. 初始化 JWT 工具

import (
    "backend/utils"
    "github.com/redis/go-redis/v9"
)

// 创建 Redis 客户端
redisClient := redis.NewClient(&redis.Options{
    Addr:     "localhost:6379",
    Password: "",
    DB:       0,
})

// 创建 JWT 工具实例
jwtUtil := utils.NewJWTUtil(
    "your-secret-key",    // 访问密钥
    7*24*60*60,          // 过期时间(秒)
    redisClient,         // Redis 客户端
)

2. 生成 Token

ctx := context.Background()
userID := int64(123)
username := "john_doe"

token, err := jwtUtil.GenerateToken(ctx, userID, username)
if err != nil {
    log.Printf("生成 token 失败: %v", err)
    return
}

fmt.Printf("生成的 token: %s\n", token)

3. 验证 Token

claims, err := jwtUtil.ValidateToken(ctx, token)
if err != nil {
    log.Printf("验证 token 失败: %v", err)
    return
}

fmt.Printf("用户ID: %d, 用户名: %s\n", claims.UserID, claims.Username)

4. 刷新 Token

newToken, err := jwtUtil.RefreshToken(ctx, oldToken)
if err != nil {
    log.Printf("刷新 token 失败: %v", err)
    return
}

fmt.Printf("新的 token: %s\n", newToken)

5. 删除 Token (登出)

err := jwtUtil.DeleteToken(ctx, token)
if err != nil {
    log.Printf("删除 token 失败: %v", err)
    return
}

fmt.Println("登出成功")

高级功能

1. 管理用户多个 Token

// 获取用户所有 token
tokens, err := jwtUtil.GetUserTokens(ctx, userID)
if err != nil {
    log.Printf("获取用户 token 失败: %v", err)
    return
}

fmt.Printf("用户拥有 %d 个 token\n", len(tokens))

// 删除用户所有 token (强制登出)
err = jwtUtil.DeleteAllUserTokens(ctx, userID)
if err != nil {
    log.Printf("删除用户所有 token 失败: %v", err)
    return
}

2. 检查 Token 是否存在

exists, err := jwtUtil.TokenExists(ctx, token)
if err != nil {
    log.Printf("检查 token 失败: %v", err)
    return
}

if exists {
    fmt.Println("Token 有效")
} else {
    fmt.Println("Token 不存在或已过期")
}

3. 解析 Token (不验证签名)

claims, err := jwtUtil.ParseTokenUnverified(token)
if err != nil {
    log.Printf("解析 token 失败: %v", err)
    return
}

fmt.Printf("Token 中的用户ID: %d\n", claims.UserID)

在 Go-Zero 服务中集成

1. 更新服务上下文

// internal/svc/servicecontext.go
type ServiceContext struct {
    Config    config.Config
    JWTUtil   *utils.JWTUtil
    // ... 其他字段
}

func NewServiceContext(c config.Config) *ServiceContext {
    // 创建 Redis 客户端
    redisClient := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
        DB:   0,
    })

    // 创建 JWT 工具
    jwtUtil := utils.NewJWTUtil(
        c.Auth.AccessSecret,
        c.Auth.AccessExpire,
        redisClient,
    )

    return &ServiceContext{
        Config:  c,
        JWTUtil: jwtUtil,
    }
}

2. 在登录逻辑中使用

// internal/logic/loginlogic.go
func (l *LoginLogic) Login(in *pb.LoginRequest) (*pb.LoginResponse, error) {
    // 验证用户凭证...

    // 生成 JWT token
    token, err := l.svcCtx.JWTUtil.GenerateToken(
        l.ctx,
        user.ID,
        user.Username,
    )
    if err != nil {
        return nil, err
    }

    return &pb.LoginResponse{
        Token: token,
        User:  user,
    }, nil
}

3. 在需要认证的逻辑中使用

// internal/logic/getprofilelogic.go
func (l *GetProfileLogic) GetProfile(in *pb.GetProfileRequest) (*pb.GetProfileResponse, error) {
    // 验证 token
    claims, err := l.svcCtx.JWTUtil.ValidateToken(l.ctx, in.Token)
    if err != nil {
        return nil, errors.New("无效的 token")
    }

    // 使用 claims 中的用户信息
    userID := claims.UserID

    // 获取用户信息...

    return &pb.GetProfileResponse{
        User: user,
    }, nil
}

中间件示例

// JWT 认证中间件
func JWTAuthMiddleware(jwtUtil *utils.JWTUtil) func(next http.HandlerFunc) http.HandlerFunc {
    return func(next http.HandlerFunc) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            // 从 Header 中获取 token
            tokenString := r.Header.Get("Authorization")
            if tokenString == "" {
                http.Error(w, "缺少 token", http.StatusUnauthorized)
                return
            }

            // 去掉 "Bearer " 前缀
            if strings.HasPrefix(tokenString, "Bearer ") {
                tokenString = tokenString[7:]
            }

            // 验证 token
            claims, err := jwtUtil.ValidateToken(r.Context(), tokenString)
            if err != nil {
                http.Error(w, "无效的 token", http.StatusUnauthorized)
                return
            }

            // 将用户信息存储到上下文中
            ctx := context.WithValue(r.Context(), "userID", claims.UserID)
            ctx = context.WithValue(ctx, "username", claims.Username)

            // 继续处理请求
            next(w, r.WithContext(ctx))
        }
    }
}

错误处理

JWT 工具返回的错误信息包括:

  • 生成 token 失败: token 生成过程中的错误
  • 存储 token 到 Redis 失败: Redis 存储错误
  • 解析 token 失败: token 格式错误或签名验证失败
  • 无效的 token: token 无效或已过期
  • token 已过期或不存在: Redis 中不存在该 token
  • token 中的用户ID与 Redis 中的不匹配: 数据不一致错误

性能优化建议

  1. Redis 连接池: 使用 Redis 连接池避免频繁创建连接
  2. Token 缓存: 对于高频访问的 token,可以考虑内存缓存
  3. 批量操作: 使用批量 Redis 操作提高性能
  4. 异步删除: 过期 token 的清理可以异步进行

安全建议

  1. 密钥管理: 使用强密钥并定期轮换
  2. HTTPS: 始终在 HTTPS 环境中传输 token
  3. 过期时间: 设置合理的 token 过期时间
  4. Redis 安全: 确保 Redis 访问安全
  5. 日志记录: 记录认证相关的操作日志

测试

运行单元测试:

cd backend/utils
go test -v

运行基准测试:

go test -bench=.

常见问题

Q: 如何处理 token 过期?

A: 使用 RefreshToken 方法刷新 token,或者重新登录获取新的 token。

Q: 如何实现强制登出?

A: 使用 DeleteAllUserTokens 方法删除用户的所有 token。

Q: Redis 连接失败怎么处理?

A: 检查 Redis 服务是否正常运行,并确保连接参数正确。

Q: Token 验证失败的常见原因?

A:

  • Token 格式错误
  • 签名密钥不匹配
  • Token 已过期
  • Redis 中不存在该 token
  • 网络连接问题

更新日志

v1.0.0

  • 基础 JWT 功能实现
  • Redis 存储支持
  • Token 刷新机制
  • 用户多 token 管理
  • 完整的测试覆盖