用Go写后台系统API--记录心得(二)

MartiniGo · 2018-11-05 00:12:43 · 16987 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2018-11-05 00:12:43 的主题,其中的信息可能已经有所发展或是发生改变。

Go写后台API

项目很渣,大佬可以提意见~~

技术栈

用的gin框架,登录验证使用jwt-go,权限管理是casbin,数据库使用mongodb

为什么要写这篇文章

因为想记录自己踩的坑,顺便记录心得。然后关于casbin这个库的使用资料很少,对于新手来说很难看明白,大神那肯定刷一遍就懂了。

正题

写到这里我们登录时差不多了。然后用postman测试一下 01.png token已经拿到了,然后也返回了一些员工的信息。前端是采用的vue 直接就可以通过axios来获取。员工信息存到vuex里,然后通过getters就能获取了。
有了增加员工,和登录。差不多了撸权限吧。这里用casbin这个库,不得不说确实很强大。但是资料真的好少啊。琢磨了好几天。让我写这篇文章的目的也是因为这个库。 casbin采用的是PERM模型来设计的。一个模型conf至少有四个部分 [request_definition], [policy_definition], [policy_effect], [matchers]如果模型使用RBAC,它也应该添加[role_definition],

请求定义

[request_definition]是访问请求的定义。它定义了函数中的参数e.Enforce(...)

[request_definition]
r = sub, obj, act

sub, obj, act,访问角色(Subject),访问的资源(Object) ,动作(Action)也可以叫访问的方法其实就是http方法GET POST那些

策略定义

其实就是定义了策略的规则。

[policy_definition]
p = sub, obj, act
p2 = sub, act

举个例子

p, alice, data1, read
p2, bob, write-all-objects

角色alice对path为data1有读的权限,角色bob可以对所有资源有wirte的权限。大概是这个意思。

策略效果

[policy_effect]是策略效果的定义。它定义了在多个策略规则与请求匹配时是否应批准访问请求。例如,一个规则允许而另一个规则否认。

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

p.eft它可以是allow或deny,它是可选的,默认是allow,这里我也没理解很透彻。所以没办法说得很清楚。大家可以研究研究。。。另一个例子

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

匹配器

[matchers]是策略匹配的定义。匹配器是表达式。它定义了如何根据请求评估策略规则。这里的语法就是正则表达式。

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

关于这个模型真的不是一两句能说完的。还是去官方看文档比我说来的快一点。。我们直接写项目来理解。
首先我们go get github.com/casbin/casbin 获取下这个包。根据说明我们要准备conf 文件。官方有个在线的editor,你可以根据选项生成conf ,因为我写的是restfulapi,所以我们采用restful的conf,把这个文件放到conf文件夹下面,给个名字auth_model.conf。

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

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

[matchers]
m = r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)

官方是定义了一个policy.csv来放策略。在实际项目中,肯定是要持久化到数据库中的,官方有很多数据库和orm的adapter。我数据库是使用mongo所以采用mongodb-adapter。 持久化到数据库,我们指定我们自己的数据库,如果数据库中不存在casbin_rule这个表它会自己创建, 先定义一个权限的结构,然后我们通过Casbin这个函数持久化到数据库并返回一个指针类型的enforcer方便我们后面调用,添加权限通过AddCasbin这个方法,把权限名,路径,方法名穿进去。

//权限结构
type CasbinModel struct {
    ID       bson.ObjectId `json:"id" bson:"_id"`
    Ptype    string        `json:"ptype" bson:"ptype"`
    RoleName string        `json:"rolename" bson:"v0"`
    Path     string        `json:"path" bson:"v1"`
    Method   string        `json:"method" bson:"v2"`
}

//添加权限
func (c *CasbinModel) AddCasbin(cm CasbinModel) bool {
    e := Casbin()
    return e.AddPolicy(cm.RoleName, cm.Path, cm.Method)

}

//持久化到数据库
func Casbin() *casbin.Enforcer {
    a := mongodbadapter.NewAdapter(mongoose.MongoUrl)
    e := casbin.NewEnforcer("conf/auth_model.conf", a)
    e.LoadPolicy()
    return e
}

权限的model差不多了。接着写api

var (
    casbins = mycasbin.CasbinModel{}
)

func AddCasbin(c *gin.Context) {
    rolename := c.PostForm("rolename")
    path := c.PostForm("path")
    method := c.PostForm("method")
    ptype := "p"
    casbin := mycasbin.CasbinModel{
        ID:       bson.NewObjectId(),
        Ptype:    ptype,
        RoleName: rolename,
        Path:     path,
        Method:   method,
    }
    isok := casbins.AddCasbin(casbin)
    if isok {
        c.JSON(http.StatusOK, gin.H{
            "success": true,
            "msg":     "保存成功",
        })
    } else {
        c.JSON(http.StatusOK, gin.H{
            "success": false,
            "msg":     "保存失败",
        })
    }

}

接着写权限验证的中间件

//权限检查中间件
func AuthCheckRole() gin.HandlerFunc {
    return func(c *gin.Context) {
        //根据上下文获取载荷claims 从claims获得role
        claims := c.MustGet("claims").(*jwt.Customclaims)
        role := claims.Role
        e := mycasbin.Casbin()
        //检查权限
        res, err := e.EnforceSafe(role, c.Request.URL.Path, c.Request.Method)
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{
                "status": -1,
                "msg":    "错误消息" + err.Error(),
            })
            c.Abort()
            return
        }
        if res {
            c.Next()
        } else {
            c.JSON(http.StatusOK, gin.H{
                "status": 0,
                "msg":    "很抱歉您没有此权限",
            })
            c.Abort()
            return
        }
    }
}

我们简单测试一下02.png

看下结果。角色是manager 路径是/apis/addemp方法是POST,差不多了,有点满意了。看下数据库09.png casbin_rule这个表里有数据了,我之前测试已经添加了2个角色一个admin一个employee。用员工的账号登录获取token然后到添加权限的路径下测试,从数据库看到employee的角色是没有PATH/apis/addrole的权限。如果看到失败那就是对的。 然后我们测试一下。 03.png 很满意了。这个结果是我预想的。后面我们还要动态的根据权限来循环生成路由。在把数据丢给前端vue处理,写的乱七八糟的。因为太困了...项目地址https://github.com/MartiniGo/leaseapp ,后续可能不会更新这个项目因为后面是业务逻辑准备放在生产环境。如果有什么新的东西新的坑我再更


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

16987 次点击  ∙  11 赞  
加入收藏 微博
14 回复  |  直到 2019-04-28 16:51:58
stu2162583
stu2162583 · #1 · 6年之前

给朵小红发,不光看,还要回帖点赞!辛苦了。

zxing2004
zxing2004 · #2 · 6年之前

不错

buscoop
buscoop · #3 · 6年之前

写得不错。继续〜

MartiniGo
MartiniGo · #4 · 6年之前
MartiniGo
MartiniGo · #5 · 6年之前

@zxing2004 感觉很多都没说明白。。因为这个库的这个模型我也没理解透彻只是个分享

MartiniGo
MartiniGo · #6 · 6年之前
gumupaier
gumupaier · #7 · 6年之前

谢谢

MartiniGo
MartiniGo · #8 · 6年之前
gumupaiergumupaier #7 回复

谢谢

客气了。只是分享下经验

zhangge3992513
zhangge3992513 · #9 · 6年之前

感谢分享

MartiniGo
MartiniGo · #10 · 6年之前
zhangge3992513zhangge3992513 #9 回复

感谢分享

不客气

yyc13579
yyc13579 · #11 · 6年之前

请问您有beego版本的权健验证中间件的代码没有?

hequan2017
hequan2017 · #12 · 6年之前

我也在用这个,看别人的例子,可以动态加载

yepinf
yepinf · #13 · 6年之前

多谢分享~

qiuax
qiuax · #14 · 6年之前

如果写在csv文件里,不写进数据库,是否更好呢?

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传