0%

Gin JWT Middleware

jwt-go中我记录了手动封装一个认证中间件,然后今天在看go-admin后台管理系统源码的时候发现作者用到了一个第三方库gin-jwt实现了同样的功能,今天就一起来看一下吧!

gin-jwt能做什么?

  • gin-jwt是基于jwt-go实现的一个认证中间件,同时也是专为Gin框架实现的。

  • gin-jwt提供了额外的函数功能:给登录接口生成token令牌,以及用于刷新token的函数。

源码分析

整个框架就一个auth_jwt.go文件,共747行代码。我们从头往下看:

先定义了一个返回值为map类型的接口
type MapClaims map[string]interface{}

接着就是一个结构体GinJWTMiddleware,还记得我们在使用jwt-go的时候,也会定义一个结构体,里面存放了关于生成token的一些信息,包括:token发放时间、是谁发放的token、主题等,这里也一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
type GinJWTMiddleware struct {
// Realm name to display to the user. Required.
Realm string

// 算法类型: HS256, HS384, HS512默认是 HS256.
SigningAlgorithm string

// 加密密钥.
Key []byte

// 刷新Token有效期.默认一个小时
MaxRefresh time.Duration

// 认证回调函数
Authenticator func(c *gin.Context) (interface{}, error)

// 通过认证后的处理函数
Authorizator func(data interface{}, c *gin.Context) bool


// 认证失败的回调函数
Unauthorized func(*gin.Context, int, string)


//登录响应函数
LoginResponse func(*gin.Context, int, string, time.Time)

// 超时响应函数
LogoutResponse func(*gin.Context, int)

// 刷新响应函数
RefreshResponse func(*gin.Context, int, string, time.Time)
}

函数(只列了几个目前见到的)

登录返回token

1
2
3
4
5
6
// LoginHandler can be used by clients to get a jwt token.
// Payload needs to be json in the form of {"username": "USERNAME", "password": "PASSWORD"}.
// Reply will be of the form {"token": "TOKEN"}.
func (mw *GinJWTMiddleware) LoginHandler(c *gin.Context) {
...
}

刷新token

1
2
3
4
5
6
7
8
9
10
11
12
// RefreshHandler can be used to refresh a token. The token still needs to be valid on refresh.
// Shall be put under an endpoint that is using the GinJWTMiddleware.
// Reply will be of the form {"token": "TOKEN"}.
func (mw *GinJWTMiddleware) RefreshHandler(c *gin.Context) {
tokenString, expire, err := mw.RefreshToken(c)
if err != nil {
mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(err, c))
return
}

mw.RefreshResponse(c, http.StatusOK, tokenString, expire)
}

验证API时需要用到,例如检测权限

1
2
3
4
5
6
// MiddlewareFunc makes GinJWTMiddleware implement the Middleware interface.
func (mw *GinJWTMiddleware) MiddlewareFunc() gin.HandlerFunc {
return func(c *gin.Context) {
mw.middlewareImpl(c)
}
}

示例

测试官方demo

源码地址:https://github.com/samtake/gin-jwt-demo

1
2
3
[GIN] 2020/04/18 - 18:29:51 | 200 |    1.502204ms |             ::1 | POST     "/login"
[GIN] 2020/04/18 - 18:30:50 | 401 | 112.029µs | ::1 | GET "/auth/hello"
[GIN] 2020/04/18 - 18:32:55 | 200 | 346.218µs | ::1 | GET "/auth/hello"

直接请求401

1
2
3
4
{
"code": 401,
"message": "cookie token is empty"
}

登录返回token

1
2
3
4
5
{
"code": 200,
"expire": "2020-04-18T19:29:51+08:00",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODcyMDkzOTEsImlkIjoiYWRtaW4iLCJvcmlnX2lhdCI6MTU4NzIwNTc5MX0.o55urrL-qhUqpqEKK8k8KF8yBqrgZo_EtKmuOW1bHnc"
}

携带token请求

1
2
3
4
5
{
"text": "Hello World.",
"userID": "admin",
"userName": "admin"
}

go-admin中的使用

这里也就记录别人在项目中是怎么使用的,以及有没有可以学习的地方。

auth.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package middleware

import (
config2 "go-admin/config"
"go-admin/handler"
jwt "go-admin/pkg/jwtauth"
"time"
)

func AuthInit() (*jwt.GinJWTMiddleware, error) {
return jwt.New(&jwt.GinJWTMiddleware{
Realm: "test zone",
Key: []byte("secret key"),
Timeout: time.Hour,
MaxRefresh: time.Hour,
IdentityKey: config2.ApplicationConfig.JwtSecret,
PayloadFunc: handler.PayloadFunc,
IdentityHandler: handler.IdentityHandler,
Authenticator: handler.Authenticator,
Authorizator: handler.Authorizator,
Unauthorized: handler.Unauthorized,
TokenLookup: "header: Authorization, query: token, cookie: jwt",
TokenHeadName: "Bearer",
TimeFunc: time.Now,
})

}

permission.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package middleware

import (
"github.com/gin-gonic/gin"
"go-admin/pkg"
mycasbin "go-admin/pkg/casbin"
"go-admin/pkg/jwtauth"
_ "go-admin/pkg/jwtauth"
"log"
"net/http"
)

//权限检查中间件
func AuthCheckRole() gin.HandlerFunc {
return func(c *gin.Context) {
data, _ := c.Get("JWT_PAYLOAD")
v := data.(jwtauth.MapClaims)
e, err := mycasbin.Casbin()
pkg.HasError(err,"",500)
//检查权限
res, err := e.Enforce(v["rolekey"], c.Request.URL.Path, c.Request.Method)
log.Println("----------------", v["rolekey"], c.Request.URL.Path, c.Request.Method)

pkg.HasError(err,"",500)

if res {
c.Next()
} else {
c.JSON(http.StatusOK, gin.H{
"status": 401,
"msg": "对不起,您没有该接口访问权限,请联系管理员",
})
c.Abort()
return
}
}
}

router.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
func InitRouter() *gin.Engine {

// the jwt middleware
authMiddleware, err := middleware.AuthInit()//初始化

if err != nil {
_ = fmt.Errorf("JWT Error", err.Error())
}

r.POST("/login", authMiddleware.LoginHandler) //框架自带

// Refresh time can be longer than token timeout
r.GET("/refresh_token", authMiddleware.RefreshHandler)//框架自带


apiv1 := r.Group("/api/v1")
{
apiv1.GET("/monitor/server", monitor.ServerInfo)
...
}


auth := r.Group("/api/v1")
auth.Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())//检测权限
{
auth.GET("/deptList", system.GetDeptList)
...
}
}

写到最后发现这里有篇博客写的挺好的,可以看看~
https://blog.firerain.me/article/18

.end