Go 服务器中基于角色的优雅 JWT 安全性

Rob*_*Rob 7 go jwt

我正在编写一个 Go web API,它使用 JWT 进行令牌/身份验证等,我想知道是否有比以下更优雅的方式来提供对我的路由处理程序的不同级别的访问?

package main

import (
    "net/http"

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

var (
    users = map[string]byte{
        "admin":    1,
        "manager":  2,
        "employee": 3,
    }
)

func main() {
    router := gin.Default()
    group := router.Group("/v1")

    jwtMiddleware := &jwt.GinJWTMiddleware{
        Realm:         "realm",
        Key:           []byte("password"),
        Authenticator: authenticate,
        PayloadFunc:   payload,
    }

    group.Use(jwtMiddleware.MiddlewareFunc())
    group.GET("/refreshToken", jwtMiddleware.RefreshHandler)
    group.GET("/hello", hello)

    router.POST("/login", jwtMiddleware.LoginHandler)
    router.Run(":1234")
}

func hello(c *gin.Context) {
    switch getRoleFromContext(c) {
    case 1:
        helloAdmin(c)
    case 2:
        helloManager(c)
    case 3:
        helloEmployee(c)
    default:
        c.AbortWithStatus(http.StatusForbidden)
    }
}

func helloAdmin(c *gin.Context)    { c.String(http.StatusOK, "Hello, admin!") }
func helloManager(c *gin.Context)  { c.String(http.StatusOK, "Hello, manager!") }
func helloEmployee(c *gin.Context) { c.String(http.StatusOK, "Hello, employee!") }

func authenticate(email string, password string, c *gin.Context) (string, bool) {
    if _, ok := users[email]; ok {
        return email, true
    }

    return "", false
}

func payload(email string) map[string]interface{} {
    return map[string]interface{}{
        "role": users[email],
    }
}

func getRoleFromContext(c *gin.Context) (role byte) {
    claims := jwt.ExtractClaims(c)

    rawRole, ok := claims["role"]
    if !ok {
        c.AbortWithStatus(http.StatusForbidden)
    }

    floatRole, ok := rawRole.(float64)
    if !ok {
        c.AbortWithStatus(http.StatusForbidden)
    }

    return byte(floatRole)
}
Run Code Online (Sandbox Code Playgroud)

在我的实际代码中(仅包含 18 个左右的路由),我创建了一个 Routes 结构,它为我的 API 提供的所有路由提供处理程序。在其中,我将上述切换逻辑执行到专门的 Route 结构,这确保只有管理用户可以执行特定功能,而其他功能可由多种类型的用户调用,只是行为不同。

我尝试将基于中间件的身份验证放在一起,如下所示:

func roleCheckerMiddleware(roles ...byte) gin.HandlerFunc {
    return func(c *gin.Context) {
        contextRole := getRoleFromContext(c)
        for _, role := range roles {
            if contextRole == role {
                c.Next()
                return
            }
        }

        c.AbortWithStatus(http.StatusForbidden)
    }
}
Run Code Online (Sandbox Code Playgroud)

...并按如下方式应用它:

adminGroup := group.Group("/")
adminGroup.GET("/hello", helloAdmin)
adminGroup.Use(roleCheckerMiddleware(1))

managerGroup := group.Group("/")
managerGroup.GET("/hello", helloManager)
managerGroup.Use(roleCheckerMiddleware(2, 1))

employeeGroup := group.Group("/")
employeeGroup.GET("/hello", helloEmployee)
employeeGroup.Use(roleCheckerMiddleware(3, 2, 1))
Run Code Online (Sandbox Code Playgroud)

...但是 /hello 路由的明显重复意味着这种方法是不可能的(据我所知),如果没有为 /admin、/manager 和 /employee 引入分组。我很乐意以其他方式显示,因为这感觉比我的切换逻辑更优雅!

小智 6

我发现使用 JWT 和/或基于 Session/Cookie 的身份验证机制使用它也非常简单。我希望这有帮助。