使用 Go 和 Redis 结合 JWT 进行权限认证| 青训营

889 阅读3分钟

简介

通过结合 Go 和 Redis 实现 JWT 权限认证,可以充分发挥二者的优势,提升系统的性能、可扩展性和安全性。同时这种设计模式也符合微服务架构和分布式系统的设计原则,使得系统更加灵活、可靠和易于维护。

使用 Go 和 Redis 结合 JWT 进行权限认证的好处:

  1. 高效性:Redis 是一种高性能的内存数据库,能够快速读写数据。结合 Redis,可以实现快速的用户登录验证和缓存用户登录信息,提高系统的响应速度和吞吐量。
  2. 可扩展性:Redis 支持分布式集群模式,并且具有良好的可扩展性。通过将用户登录信息存储在 Redis 集群中,可以轻松地扩展系统的处理能力,适应高并发的用户访问。
  3. 灵活性:Redis 提供了丰富的数据结构和功能,例如字符串、哈希、列表、集合等。这使得可以根据具体需求灵活地选择合适的数据结构来存储用户登录信息,在不同场景下进行查询和操作。
  4. 可靠性:Redis 支持数据持久化,可以将缓存的用户登录信息保存到磁盘中,以防止系统重启或故障时数据丢失。这样可以确保用户的登录状态得到可靠的保护,提高系统的稳定性和可用性。
  5. 安全性:JWT 使用签名和密钥进行验证,可以有效防止伪造和篡改。而将 JWT 放置在 Redis 中,对于已登录的用户,可以通过验证 Redis 中的令牌和用户ID来快速确定其身份和权限,而无需每次请求都对 JWT 进行解析和验证。

步骤

首先需要导入相关库,包括 github.com/dgrijalva/jwt-gogithub.com/go-redis/redis

import (
    "github.com/dgrijalva/jwt-go"
    "github.com/go-redis/redis"
)

然后定义常量:包括 JWT 密钥和 Redis 过期时间:

const jwtSecret = "your-jwt-secret-key"
const redisExpiration = 3600 // 过期时间,单位为秒

创建一个 Redis 客户端连接:

func NewRedisClient() *redis.Client {
    client := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379", // Redis 服务器地址和端口
        Password: "",               // 如果 Redis 密码为空,则不需要设置
        DB:       0,                // 使用默认的数据库
    })

    _, err := client.Ping().Result()
    if err != nil {
        panic(err)
    }

    return client
}

定义方法:实现 JWT 的签发、验证和缓存。

签发 JWT

func GenerateJWT(userID string) (string, error) {
    token := jwt.New(jwt.SigningMethodHS256)
    claims := token.Claims.(jwt.MapClaims)
    claims["userID"] = userID
    // 设置过期时间
    claims["exp"] = time.Now().Add(time.Second * redisExpiration).Unix()

    tokenString, err := token.SignedString([]byte(jwtSecret))
    if err != nil {
        return "", err
    }
    return tokenString, nil
}

验证 JWT

func VerifyJWT(tokenString string) (string, error) {
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        return []byte(jwtSecret), nil
    })

    if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
        userID := claims["userID"].(string)
        return userID, nil
    }

    return "", fmt.Errorf("invalid token")
}

缓存用户登录信息

func CacheLoginInfo(client *redis.Client, userID string, token string) error {
    err := client.Set(userID, token, time.Second*redisExpiration).Err()
    if err != nil {
        return err
    }
    return nil
}

获取用户登录信息

func GetLoginInfo(client *redis.Client, userID string) (string, error) {
    token, err := client.Get(userID).Result()
    if err != nil {
        return "", err
    }
    return token, nil
}

删除用户登录信息

func RemoveLoginInfo(client *redis.Client, userID string) error {
    err := client.Del(userID).Err()
    if err != nil {
        return err
    }
    return nil
}

使用示例:使用 JWT 来生成和验证令牌,并将令牌缓存在 Redis 中。

func main() {
    // 建立 Redis 连接
    client := NewRedisClient()

    // 模拟用户登录,生成 JWT
    token, err := GenerateJWT("user123")
    if err != nil {
        fmt.Println(err)
    }

    // 缓存用户登录信息
    err = CacheLoginInfo(client, "user123", token)
    if err != nil {
        fmt.Println(err)
    }

    // 解析和验证 JWT
    userID, err := VerifyJWT(token)
    if err != nil {
        fmt.Println("JWT verification failed!")
    } else {
        fmt.Println("UserID:", userID)
    }

    // 获取用户登录信息
    cachedToken, err := GetLoginInfo(client, "user123")
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println("Cached Token:", cachedToken)
    }

    // 删除用户登录信息
    err = RemoveLoginInfo(client, "user123")
    if err != nil {
        fmt.Println(err)
    }

    // 再次获取用户登录信息
    cachedToken, err = GetLoginInfo(client, "user123")
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Token after removal:", cachedToken)
    }
}