casbin訪問控制框架在GO中的基本使用方法

我對casbin的理解

本文以iris框架作爲示例,原生或其它框架基本是一樣的。

根據官方文檔,首先介紹一下幾個重要部位。(安裝方法跳過)

使用casbin有兩個地方是需要配置的,一個是model,另一個是policy

我們做權限控制用得比較多的是RBAC(基於角色的權限控制)

看過官方文檔的同學都知道,model的內容來源可以是.conf文件,也可以是在代碼中編寫;但是policy內容應該是動態的,可以隨時更新的,把它放.csv文件裏管理起來多不方便?稍安勿躁,後面會說說我的解決方法。

簡單的測試

先看看別人怎麼做的

package main

import (
    "github.com/kataras/iris"

    "github.com/casbin/casbin"
    cm "github.com/iris-contrib/middleware/casbin"
)

var Enforcer = casbin.NewEnforcer("casbin_model.conf", "casbi_npolicy.csv")

func newApp() *iris.Application {
    casbinMiddleware := cm.New(Enforcer)
    app := iris.New()
    app.WrapRouter(casbinMiddleware.Wrapper())
    app.Get("/", hi)
    app.Any("/dataset1/{p:path}", hi)
    app.Post("/dataset1/resource1", hi)
    app.Get("/dataset2/resource2", hi)
    app.Post("/dataset2/folder1/{p:path}", hi)
    app.Any("/dataset2/resource1", hi)

    return app
}

func main() {
    app := newApp()
    app.Run(iris.Addr(":8080"))
}

func hi(ctx iris.Context) {
    ctx.Writef("Hello %s", cm.Username(ctx.Request()))
}

上面是iris文檔上的一段示例代碼,可能版本不一樣了,我運行下面的代碼會直接報錯,原因是github.com/iris-contrib/middleware/casbin/casbin.go裏面的一個方法:

func (c *Casbin) Check(r *http.Request) bool {
    username := Username(r)
    method := r.Method
    path := r.URL.Path
    b:=c.enforcer.Enforce(username, path, method)//這裏c.enforcer.Enforce返回兩個值,一個是bool,另一個是error,這裏只定義了一個,所以報錯了
    return b
}

還有一個問題是,這個中間件默認採用BasicAuth獲取用戶名的,如果你也是採用這種方法,那就大吉大利了,如果不是,還是自己動手取用戶名吧

既然是一個簡單的測試,那就簡單粗暴的直接改寫整個casbin.go文件

開始動手

/rbac_model.conf

//RBAC1

//請求定義
//sub 想要訪問資源的用戶
//obj 要訪問的資源
//act 用戶對資源執行的操作,act可以是read、write、print等等你想要自定義的操作
[request_definition]
r = sub, obj, act

//策略定義,也就是*.cvs文件 p 定義的格式
[policy_definition]
p = sub, obj, act

//組定義,也就是*.cvs文件 g 定義的格式。g是用戶組或角色
[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub) && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*")

/rbac_policy.csv (註釋刪掉)

p,abc123,/user,GET //用戶abc123對/user有GET權限,下面同理
p,abc123,/user,POST
p,admin,/test,* //角色或用戶組admin對/test有所有權限
g,super_admin,admin //用戶super_admin屬於admin組或角色

複製整個casbin.go文件到自己的項目下,我把它放在/middleware/裏面了

/middleware/casbin.go

package middleware

import (
    "github.com/casbin/casbin"
    "github.com/kataras/iris/context"
    "net/http"
)

func New(e *casbin.Enforcer) *Casbin {
    return &Casbin{enforcer: e}
}

func (c *Casbin) Wrapper() func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {
    return func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {
        if !c.Check(r) {
            w.WriteHeader(http.StatusForbidden)
            _, _ = w.Write([]byte("403 Forbidden"))
            return
        }
        router(w, r)
    }
}

func (c *Casbin) ServeHTTP(ctx context.Context) {
    if !c.Check(ctx.Request()) {
        ctx.StatusCode(http.StatusForbidden) // Status Forbiden
        ctx.StopExecution()
        return
    }
    ctx.Next()
}

type Casbin struct {
    enforcer *casbin.Enforcer
}

func (c *Casbin) Check(r *http.Request) bool {
    username := Username(r)
    method := r.Method
    path := r.URL.Path
    b,_:=c.enforcer.Enforce(username, path, method)
    return b
}

func Username(r *http.Request) string {
    //username, _, _ := r.BasicAuth()//這玩意我用不上,把它註釋掉
    return "abc123" //直接返回用戶名,看看測試效果
}

main.go

package main

import (
    "github.com/kataras/iris"

    "github.com/casbin/casbin"
    cm "project_path/middleware/casbin"
)

var e = casbin.NewEnforcer("casbin_model.conf", "casbi_npolicy.csv")

func newApp() *iris.Application {
    casbinMiddleware := cm.New(e)
    app := iris.New()
    app.WrapRouter(casbinMiddleware.Wrapper())
    // 如果不想使用中間件,可以通過下面方法進行判斷
    /*
    if b,err:=e.Enforce("abc123","/user","Get");b {
        fmt.Println("成功")
    } else {
        fmt.Println("失敗")
    }
    */
    app.Get("/user", hi)
    app.Post("/user", hi)
    app.Put("/test", hi)

    return app
}

func main() {
    app := newApp()
    app.Run(iris.Addr(":8080"))
}

func hi(ctx iris.Context) {
    ctx.Writef("當你看到這個,說明通過了權限驗證")
}

,就這麼簡單,下面使用
Get訪問localhost:8080/user
Post訪問localhost:8080/user
上面兩個訪問都成功通過驗證了,下面的
Get、Post、Put、Delete等等訪問localhost:8080/test
都返回了403 Forbidden,當把Username方法改爲return "super_admin"再訪問試試。

現實項目中,可能會把Check方法中的username改爲用戶ID或者郵箱或者手機號都是可以的。

policy 待續...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章