Gin使用自定义中间件--JWT

代码

package main

import (
	"errors"
	"net/http"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt"
)

func main() {
	// gin.Default 会使用Logger, Recover中间件,
	// 这里改用gin.New 新建一个没有任何默认中间件的路由
	r := gin.New()

	// 全局中间件
	// Logger 中间件将日志写入 gin.DefaultWriter,即使你将 GIN_MODE 设置为 release。
	// By default gin.DefaultWriter = os.Stdout
	r.Use(gin.Logger())

	// Recovery 中间件会 recover 任何 panic。如果有 panic 的话,会写入 500。
	r.Use(gin.Recovery())

	// 定义路由组
	v1 := r.Group("/v1")
	{
		v1.POST("/auth", func(c *gin.Context) {
			username, ok := c.GetPostForm("username")
			if !ok {
				c.JSON(400, gin.H{"msg": "The form is missing username."})
				return
			}
			password, ok := c.GetPostForm("password")
			if !ok {
				c.JSON(400, gin.H{"msg": "The form is missing password."})
				return
			}

			func(u, p string) {}(username, password) // 验证username, password, 没写验证逻辑

			tokenString, err := generateToken(username, time.Hour*24*7)
			if err != nil {
				c.JSON(400, gin.H{
					"msg":      err.Error(),
					"username": username,
				})
				return
			}
			c.JSON(200, gin.H{
				"token":    tokenString,
				"username": username,
			})
		})
		v1.GET("/home", JWTAuthMiddleware(), func(c *gin.Context) {
			username, _ := c.Get("username")

			c.JSON(200, gin.H{
				"status":   "ok",
				"username": username,
			})
		})
	}
	r.Run(":8080")
}

// For HMAC signing method, the key can be any []byte. It is recommended to generate
// a key using crypto/rand or something equivalent. You need the same key for signing
// and validating.
var hmacSampleSecret []byte = []byte(`key`)

type MyClaims struct {
	Username string `json:"username"`
	jwt.StandardClaims
}

// 生成 Token
func generateToken(userName string, expire time.Duration) (tokenString string, err error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, MyClaims{
		userName,
		jwt.StandardClaims{
			NotBefore: time.Now().Unix(),
			ExpiresAt: time.Now().Add(expire).Unix(),
		},
	})

	// Sign and get the complete encoded token as a string using the secret
	tokenString, err = token.SignedString(hmacSampleSecret)
	return
}

// ParseToken 解析JWT
func ParseToken(tokenString string) (*MyClaims, error) {
	// 解析token
	token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (i interface{}, err error) {
		return hmacSampleSecret, nil
	})
	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(*MyClaims); ok && token.Valid { // 校验token
		return claims, nil
	}
	return nil, errors.New("invalid token")
}

// JWTAuthMiddleware 基于JWT的认证中间件
func JWTAuthMiddleware() func(c *gin.Context) {
	return func(c *gin.Context) {
		// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI
		// 这里假设Token放在Header的Authorization中,并使用Bearer开头
		// 这里的具体实现方式要依据你的实际业务情况决定
		authHeader := c.Request.Header.Get("Authorization")
		if authHeader == "" {
			c.JSON(http.StatusOK, gin.H{
				"code": 2003,
				"msg":  "请求头中auth为空",
			})
			c.Abort()
			return
		}

		// 按空格分割
		parts := strings.SplitN(authHeader, " ", 2)
		if !(len(parts) == 2 && parts[0] == "Bearer") {
			c.JSON(http.StatusOK, gin.H{
				"code": 2004,
				"msg":  "请求头中auth格式有误",
			})
			c.Abort()
			return
		}

		// parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它
		mc, err := ParseToken(parts[1])
		if err != nil {
			c.JSON(http.StatusOK, gin.H{
				"code": 2005,
				"msg":  "无效的Token",
			})
			c.Abort()
			return
		}

		// 将当前请求的username信息保存到请求的上下文c上
		c.Set("username", mc.Username)
		c.Next() // 后续的处理函数可以用过c.Get("username")来获取当前请求的用户信息
	}
}

上一篇:LVM逻辑卷与磁盘配额


下一篇:使用 go 协程+Channel,让你的代码执行快到起飞