记录一次Casbin实现ABAC模型的实践

2,770 阅读4分钟

转载请注明出处 juejin.cn/post/687408…

前阵子尝试了下Casbin,感觉挺简单易用的,功能也很强大,由于我的主要开发语言是Java,但是网上关于JCasbin的文章又比较少,就打算自己写一篇,记录一下。

工作原理

根据Casbin的官方文档所述,Casbin的访问模型被抽象为一个基于PERM (Policy, Effect, Request, Matcher) 的文件

举个简单的例子,现在的业务场景是这样,要定义员工A能够访问杭州区域的销售数据。

Request开始,组成请求需要有4个元素,unit(区域),user(用户),obj(客体),act(动作)。

Policy,即策略,组成它的元素是Request的组成元素的特化,比如m1(员工A)对应user,

hangzhou(杭州区域)对应unit,/sells/list(销售数据)对应obj,GET(动作)对应act。

Matcher, 即匹配规则,在JCasbin中是AviatorScript实现的,所以具体语法也可参见该脚本语言的文档,简单例子如下,keyMatch是匹配函数,,我这边用于url匹配,regexMatch顾名思义是正则函数,整个匹配规则的语义就是,当请求中携带的unit和user与策略中的unit和user相同,且两者的obj在keyMatch函数中返回true,act在regexMatch中返回true的时候,视为匹配

m = r.unit == p.unit  && r.user == p.user && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)

Effect,代表策略生效范围,当我使用如下规则的时候,代表的是Policy的每一个元素都要完全符合Matcher, 才能返回allow

e = some(where (p.eft == allow))

那么在什么情况下,会需要调整策略生效范围呢?

一般是用于XXX条件下允许做什么 但是 XXX条件下除外这种语境,这边稍微说详细点,我现在有如下Policy定义

[policy_definition]
p = unit, user, obj, act, eft

有如下Effect

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

有如下Policy规则

p, hangzhou, m1, /sells/*, POST|GET|PUT //允许员工m1对杭州地区/sells/*下的所有接口进行访问修改等操作
p, hangzhou, m1, /sells/info, POST, deny //拒绝员工m1对杭州地区/sells/info接口进行提交操作

Request的参数和结果如下

hangzhou, m1, /sells/list, POST //结果为true
hangzhou, m1, /sells/info, POST  //结果为false

可以看到,在该Effect规则下,如果既存在allow又存在deny,那么就用deny覆盖allow

灵活使用规则,可以在做权限配置的时候省力不少,对于上了规模的公司,假如使用ABAC模型做主要的访问控制策略,并且仅做白名单形式的权限配置的话,那么policy总数过万是不在话下的。

持久化

  • Model持久化

根据官方文档的描述,model只能加载,不能保存,设计人员认为model不是动态组件,不应当在运行时修改,所以他们也没有做持久化model的api。对此观点我也是赞同的,一般来说模型不会需要实时变更。

但是对于需要做持久化保存和运行时加载的同学来说,也可以绕个弯实现。

官方比较推荐的是从.conf文件加载,那么我们可以从文件服务器那边(如果有的话)加载过来。

也可以通过代码初始化,比如:

Model model = new Model();
model.addDef("r", "r", "unit, user, obj, act");
model.addDef("p", "p", "unit, user, obj, act");
model.addDef("e", "e", "some(where (p.eft == allow))");
model.addDef("m", "m", "m = r.unit == p.unit  && r.user == p.user && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)");

还可以从字符串当中加载,所以如果要存DB的话,就用字符串的形式把model存进去,需要的时候再取出来重新加载。

private Model buildModelFromText(String text) {
    Model model = new Model();
    model.loadModelFromText(text);
    return model;
}
  • Policy持久化

官方提供了多种类型的Adaptor来实现Policy的持久化,比如JDBC,Hibernate,Mybatis,有人做了一个SpringBoot-Starter组件,组件会根据model建立合适的规则表,但是我个人觉得传统关系型数据库不太适合做它的持久化,可能文档数据库的效果会更好,如果有人能尝试下MongoDB做持久化的话可以留言告诉我下效果。

后面就直接贴代码吧,已上传github(请勿直接用于生产环境)

做了一个简单的demo,建表sql和RequestSample可以见resource文件下面的sql文件和ExampleRequest。

github.com/LLLLimbo/ca…