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

MartiniGo · · 746 次点击 · 开始浏览    置顶
## Go写后台API 项目很渣,大佬可以提意见~~ ### 技术栈 用的gin框架,登录验证使用jwt-go,权限管理是casbin,数据库使用mongodb ### 为什么要写这篇文章 因为想记录自己踩的坑,顺便记录心得。然后关于casbin这个库的使用资料很少,对于新手来说很难看明白,大神那肯定刷一遍就懂了。 ### 正题 写到这里我们登录时差不多了。然后用postman测试一下 ![01.png](https://static.studygolang.com/181104/05b025f000f75e61cd4fdb079e4d5094.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`这个方法,把权限名,路径,方法名穿进去。 ```go //权限结构 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 ```go 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": "保存失败", }) } } ``` 接着写权限验证的中间件 ```go //权限检查中间件 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](https://static.studygolang.com/181105/ed775eef7427a4ff6f9b552eb59238fb.png) 看下结果。角色是`manager` 路径是`/apis/addemp`方法是`POST`,差不多了,有点满意了。看下数据库![09.png](https://static.studygolang.com/181105/967d30d51cb6528f675dde40fb00e057.png) casbin_rule这个表里有数据了,我之前测试已经添加了2个角色一个`admin`一个`employee`。用员工的账号登录获取token然后到添加权限的路径下测试,从数据库看到`employee`的角色是没有PATH`/apis/addrole`的权限。如果看到失败那就是对的。 然后我们测试一下。 ![03.png](https://static.studygolang.com/181104/51bad599906e1ccd1e22870126e86dec.png) 很满意了。这个结果是我预想的。后面我们还要动态的根据权限来循环生成路由。在把数据丢给前端vue处理,写的乱七八糟的。因为太困了...项目地址https://github.com/MartiniGo/leaseapp ,后续可能不会更新这个项目因为后面是业务逻辑准备放在生产环境。如果有什么新的东西新的坑我再更
746 次点击  ∙  5 赞  
加入收藏 微博
10 回复  |  直到 2018-11-09 16:56:01
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传