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

时间:2016-11-18 14:05:42

标签: go jwt

我正在编写一个使用JWT作为令牌/身份验证等的Go Web API。我想知道是否有更优雅的方式为我的路由处理程序提供不同级别的访问权限而不是以下内容?

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)
}

在我的实际代码中(仅包含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)
    }
}

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

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))

...但是/ hello路由的明显重复意味着这种方法不是(据我所知)可能的,没有为/ admin,/ manager和/ employee引入分组。我希望能够以其他方式表现出来,因为这感觉我的切换逻辑更加优雅!

2 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:0)

只需使用github.com/casbin/casbin即可提高您的授权和身份验证中间件的精度,因为它也对我有很大帮助。

并为使用它提供更多说明性示例;使用https://zupzup.org/casbin-http-role-auth/