实践记录2:中间件jwt实践 | 青训营

99 阅读3分钟

在实现了资产管理系统大体的业务之后,发现用户的鉴权可以直接使用jwt。下面简单介绍以下go-jwt和我在资产管理系统中是如何落地jwt的。

jwt介绍

JSON Web Token(JWT)是一种用于在网络应用间传递信息的开放标准。它是一种轻量级的数据交换格式,通常由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。

1. 头部(Header): 头部通常由两部分组成:令牌的类型(即 "typ" 字段,通常为 "JWT")和使用的签名算法(即 "alg" 字段,例如 "HS256" 表示使用 HMAC-SHA256 算法)。

{
  "alg": "HS256",
  "typ": "JWT"
}

2. 载荷(Payload): 载荷是 JWT 的第二部分,包含要传递的数据。载荷可以包含称为 "claims" 的声明,这些声明描述了关于实体(通常是用户)和其他数据的一些信息。有三种类型的声明:注册声明、公开声明和私有声明。注册声明是预定义的,用于标识 JWT 的信息,例如 "iss"(签发者)、"exp"(过期时间)等。公开声明可以自由定义。私有声明用于在双方之间交换信息,通常不会被标准规范使用。

{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }

3. 签名(Signature): JWT 的签名部分使用头部和载荷,以及一个密钥,通过指定的签名算法生成。签名的目的是验证发送者是否是该令牌的有效签发者,以及确保在传输过程中数据没有被篡改。

生成签名的过程:HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

最终生成的 JWT 结构:base64UrlEncode(header) + "." + base64UrlEncode(payload) + "." + signature

go-jwt的实践

首先

go get github.com/appleboy/gin-jwt/v2

然后这样引入

import(
    ...
    jwt "github.com/appleboy/gin-jwt/v2"
)

下面是这个中间件使用的全部代码,看不懂没关系,我会慢慢解释。

package middleware

import (
	"log"
	"msbams/src/dbconn"
	"msbams/src/entity"
	"msbams/src/gen/model"
	"time"

	jwt "github.com/appleboy/gin-jwt/v2"
	"github.com/gin-gonic/gin"
)

var IdentityKey = "username"
var AuthMiddleware *jwt.GinJWTMiddleware

type AccountForJwt struct {
	Username string
	Name     string
	Id       int32
	Type     int32
}

func init() {
	// the jwt middleware
	var err error
	AuthMiddleware, err = jwt.New(&jwt.GinJWTMiddleware{
		Realm:       "test zone",
		Key:         []byte("msbams.,."),
		Timeout:     time.Hour,
		MaxRefresh:  time.Hour,
		IdentityKey: IdentityKey,
		PayloadFunc: func(data interface{}) jwt.MapClaims {
			// fmt.Println("PayloadFunc", data)

			if v, ok := data.(*AccountForJwt); ok { // 登录成功后,会调用这个函数
				return jwt.MapClaims{
					IdentityKey: v.Username,
					"id":        v.Id,
					"name":      v.Name,
					"type":      v.Type,
				}
			}
			return jwt.MapClaims{}
		},
		IdentityHandler: func(c *gin.Context) interface{} { //鉴权的时候会自动调用这个
			// fmt.Println("IdentityHandler")
			claims := jwt.ExtractClaims(c)
			return &AccountForJwt{
				Username: claims[IdentityKey].(string),
				Id:       int32(claims["id"].(float64)),
				Name:     claims["name"].(string),
				Type:     int32(claims["type"].(float64)),
			}
		},
		Authenticator: func(c *gin.Context) (interface{}, error) {
			login_req := entity.AccountRequest{}
			err := c.BindJSON(&login_req)
			if err != nil || login_req.Password == "" || login_req.Username == "" {
				return "", jwt.ErrMissingLoginValues
			}

			acc := model.Account{}
			res := dbconn.DB.Where("username = ?", login_req.Username).First(&acc)

			if res.Error != nil {
				return nil, jwt.ErrFailedAuthentication
			}
			if acc.Password == login_req.Password {
				// fmt.Println("login success", acc)

				return &AccountForJwt{
					Username: acc.Username,
					Name:     acc.Name,
					Id:       acc.ID,
					Type:     acc.Type,
				}, nil
			}
			return nil, jwt.ErrFailedAuthentication
		},
		Authorizator: func(data interface{}, c *gin.Context) bool {
			// fmt.Println("Authorizator")
			if v, ok := data.(*AccountForJwt); ok && v.Username != "" {
				return true
			}
			return false
		},
		Unauthorized: func(c *gin.Context, code int, message string) {
			c.JSON(code, entity.Result{
				Code:        code,
				Description: message,
			})
		},
		TokenLookup:   "header: Authorization, query: token, cookie: token",
		TokenHeadName: "Bearer",
		TimeFunc:      time.Now,
	})

	if err != nil {
		log.Fatal("JWT Error:" + err.Error())
	}

	errInit := AuthMiddleware.MiddlewareInit()
	if errInit != nil {
		panic("authMiddleware.MiddlewareInit() Error:" + errInit.Error())
	}
}

这段代码实现了基于 JWT 的身份验证和授权,它会在用户登录成功后生成一个 JWT 令牌,并在每次请求时验证令牌的有效性,以确定用户是否有权限访问特定资源。如果令牌有效且用户有权限,则允许用户访问资源。否则,返回未授权错误403。

emmm那么我们如何在service层中使用呢?很简单:

user, _ := c.Get(middleware.IdentityKey)
accountId := user.(*middleware.AccountForJwt).Id

即可。其中middleware是你的中间件所在的的包名。

如何实现它作为中间件的价值呢?只需要调用路由的Use函数即可:

//采购相关
purchaseGroup := r.Group("/purchase")
purchaseGroup.Use(middleware.AuthMiddleware.MiddlewareFunc())