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

weixin_38063296 2019-09-20 09:59:57
## Go写后台API
项目很渣,大佬可以提意见~~
### 技术栈
用的gin框架,登录验证使用jwt-go,权限管理是casbin,数据库使用mongodb
### 为什么要写这篇文章
因为想记录自己踩的坑,顺便记录心得。然后关于casbin这个库的使用资料很少,对于新手来说很难看明白,大神那肯定刷一遍就懂了。
### 正题
写到这里我们登录时差不多了。然后用postman测试一下

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
}
}
}
```
我们简单测试一下

看下结果。角色是`manager` 路径是`/apis/addemp`方法是`POST`,差不多了,有点满意了。看下数据库
casbin_rule这个表里有数据了,我之前测试已经添加了2个角色一个`admin`一个`employee`。用员工的账号登录获取token然后到添加权限的路径下测试,从数据库看到`employee`的角色是没有PATH`/apis/addrole`的权限。如果看到失败那就是对的。
然后我们测试一下。

很满意了。这个结果是我预想的。后面我们还要动态的根据权限来循环生成路由。在把数据丢给前端vue处理,写的乱七八糟的。因为太困了...项目地址https://github.com/MartiniGo/leaseapp ,后续可能不会更新这个项目因为后面是业务逻辑准备放在生产环境。如果有什么新的东西新的坑我再更
...全文
89 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
weixin_38120800 2019-09-20
  • 打赏
  • 举报
回复
不客气
weixin_38132030 2019-09-20
  • 打赏
  • 举报
回复
请问您有beego版本的权健验证中间件的代码没有?
weixin_38138286 2019-09-20
  • 打赏
  • 举报
回复
我也在用这个,看别人的例子,可以动态加载
weixin_38143269 2019-09-20
  • 打赏
  • 举报
回复
多谢分享~
weixin_38152961 2019-09-20
  • 打赏
  • 举报
回复
如果写在csv文件里,不写进数据库,是否更好呢?
weixin_38067400 2019-09-20
  • 打赏
  • 举报
回复
给朵小红发,不光看,还要回帖点赞!辛苦了。
weixin_38073387 2019-09-20
  • 打赏
  • 举报
回复
不错
weixin_38092208 2019-09-20
  • 打赏
  • 举报
回复
写得不错。继续〜
weixin_38099114 2019-09-20
  • 打赏
  • 举报
回复
谢谢
weixin_38103381 2019-09-20
  • 打赏
  • 举报
回复
客气了。只是分享下经验
weixin_38108951 2019-09-20
  • 打赏
  • 举报
回复
感谢分享

433

社区成员

发帖
与我相关
我的任务
社区描述
其他技术讨论专区
其他 技术论坛(原bbs)
社区管理员
  • 其他技术讨论专区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧