golang,go,博客,开源,编程

gin中间件之jwt认证

Published on with 0 views and 0 comments

Gin 中实现 JWT(JSON Web Token)认证 中间件,主要目的是通过解析请求中的 JWT token,验证其合法性,进而判断请求是否被授权访问某些受保护的资源。JWT 是一种紧凑、安全的 URL-safe 的方式,用于表示通过 JSON 对象传递的声明。JWT 可以用于身份验证和信息交换。

1. JWT 认证中间件实现

我们可以编写一个自定义中间件来实现 JWT 认证。常见的实现流程如下:

  1. 从请求头中提取 Authorization 字段。
  2. 提取 Bearer token 并解析。
  3. 使用密钥验证 JWT 的有效性(签名、过期时间等)。
  4. 如果验证通过,则继续处理请求,否则返回 401 错误。

2. 实现 JWT 中间件的示例代码

以下是基于 Gin 的 JWT 认证中间件示例:

package main

import (
	"fmt"
	"github.com/dgrijalva/jwt-go" // 你可以使用其他库,dgrijalva/jwt-go 是常用的 Go JWT 库
	"github.com/gin-gonic/gin"
	"net/http"
	"strings"
	"time"
)

var jwtSecret = []byte("your-secret-key") // 用于签名和验证 JWT 的密钥

// JWT 中间件
func JWTMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 从 Authorization header 中获取 token
		authHeader := c.GetHeader("Authorization")
		if authHeader == "" {
			c.JSON(http.StatusUnauthorized, gin.H{
				"message": "Authorization header is missing",
			})
			c.Abort()
			return
		}

		// Bearer token 格式:Authorization: Bearer <token>
		parts := strings.Split(authHeader, " ")
		if len(parts) != 2 || parts[0] != "Bearer" {
			c.JSON(http.StatusUnauthorized, gin.H{
				"message": "Invalid Authorization header format",
			})
			c.Abort()
			return
		}

		// 获取 JWT token
		tokenString := parts[1]

		// 解析 JWT token
		token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
			// 校验 token 签名算法是否正确
			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
			}
			return jwtSecret, nil
		})

		if err != nil || !token.Valid {
			c.JSON(http.StatusUnauthorized, gin.H{
				"message": "Invalid token",
			})
			c.Abort()
			return
		}

		// 如果验证通过,可以将 token 中的信息放到 context 中
		if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
			// 将用户信息(如用户 ID)保存到上下文中,供后续的路由使用
			c.Set("user_id", claims["user_id"])
			c.Set("username", claims["username"])
		}

		// 继续处理请求
		c.Next()
	}
}

// 模拟生成 JWT token
func GenerateJWT(userID string, username string) (string, error) {
	// 创建一个声明(Claims)
	claims := jwt.MapClaims{}
	claims["user_id"] = userID
	claims["username"] = username
	claims["exp"] = time.Now().Add(time.Hour * 24).Unix() // 设置过期时间

	// 创建 token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// 使用 secret 密钥签名并生成 token 字符串
	return token.SignedString(jwtSecret)
}

func main() {
	// 创建 Gin 实例
	r := gin.Default()

	// 公共路由:无需验证的接口
	r.GET("/login", func(c *gin.Context) {
		// 模拟用户登录,生成 JWT
		token, err := GenerateJWT("12345", "user_name")
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"message": "Failed to generate token",
			})
			return
		}

		c.JSON(http.StatusOK, gin.H{
			"token": token,
		})
	})

	// 受保护路由:需要 JWT 认证的接口
	protected := r.Group("/protected")
	protected.Use(JWTMiddleware()) // 使用 JWT 中间件进行认证

	protected.GET("/data", func(c *gin.Context) {
		userID, _ := c.Get("user_id")
		username, _ := c.Get("username")

		c.JSON(http.StatusOK, gin.H{
			"user_id":  userID,
			"username": username,
			"message":  "This is protected data",
		})
	})

	// 启动 Gin 服务
	r.Run(":8080")
}

3. 代码解析

  • GenerateJWT 函数
    • 这个函数用于模拟生成一个 JWT。在实际应用中,你可以在用户登录时生成该 JWT。
    • 我们使用 jwt.MapClaims 来创建一个包含 user_idusernameexp(过期时间)等信息的声明。
    • 使用 jwt.NewWithClaims 来创建一个新的 JWT,并通过密钥 jwtSecret 对其进行签名。
  • JWTMiddleware 中间件
    • 该中间件从请求头中的 Authorization 字段提取 Bearer token。
    • 使用 jwt.Parse 解析 token,并验证其签名和有效性。
    • 如果 token 有效,将其包含的 user_idusername 等信息存储到 Gin 的上下文中(c.Set),供后续路由使用。
  • /login 路由
    • 模拟用户登录并生成 JWT。在实际应用中,你可以根据用户名和密码验证用户,并生成 JWT。
  • /protected/data 路由
    • 这是一个受保护的路由,只有携带有效 JWT 的请求才能访问。
    • 如果 JWT 验证通过,用户信息会被提取并返回。

4. 启动服务

  1. 启动服务后,访问 http://localhost:8080/login 会生成一个 JWT。

    curl http://localhost:8080/login
    

    返回一个 JSON 响应,包含生成的 token:

    {
      "token": "your-jwt-token"
    }
    
  2. 使用返回的 token 访问 /protected/data 路由:

    curl -H "Authorization: Bearer your-jwt-token" http://localhost:8080/protected/data
    

    如果 token 验证通过,返回:

    {
      "user_id": "12345",
      "username": "user_name",
      "message": "This is protected data"
    }
    
  3. 如果没有提供有效的 token,则返回 401 错误:

    curl http://localhost:8080/protected/data
    

    返回:

    {
      "message": "Authorization header is missing"
    }
    

5. 注意事项

  • 密钥管理:密钥 jwtSecret 在生产环境中应该保密,可以使用环境变量或配置文件来管理密钥。
  • 过期时间:JWT 通常会设置过期时间(exp),过期后需要重新登录或刷新 token。
  • 签名算法:示例中使用的是 HMAC SHA256 算法(SigningMethodHS256),如果你需要使用其他算法(例如 RSA),可以在 jwt.NewWithClaims 中选择不同的算法。
  • 用户身份管理:JWT 的有效性验证仅仅是安全控制的一部分,真正的身份管理和权限控制需要根据用户角色和权限来实现。

总结

通过在 Gin 中实现 JWT 认证中间件,可以保护你的 API 接口,确保只有经过身份验证的用户才能访问受保护的资源。JWT 提供了简便、无状态的身份认证方式,适合现代 Web 应用和微服务架构。


标题:gin中间件之jwt认证
作者:mooncakeee
地址:http://blog.dd95828.com/articles/2025/01/07/1736218635987.html
联系:scotttu@163.com