jcasbin 1.99.0 快速入门与集成示例
jcasbin 1.99.0 快速入门:依赖引入与核心概念,一个类跑通内置匹配函数、RESTful、keyMatch、RBAC deny-override、显式优先级、ABAC 五组模型,Management/RBAC API 速查,全程实测。
jcasbin 1.99.0 快速入门与集成示例
目标:用一篇文档完成 jcasbin 1.99.0(Apache Casbin 的 Java 实现)的依赖引入与核心 API 上手——一个覆盖内置匹配函数、RESTful、keyMatch、RBAC deny-override、显式优先级、ABAC 的示例类可整体复制运行,已在 Ubuntu 22.04(OpenJDK 11.0.32.1、Maven 3.6.3)实测通过。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(jammy),x86_64
- JDK:OpenJDK 11.0.32.1(jcasbin 1.99.0 字节码为 Java 8,JDK 8+ 均可运行本篇示例)
- 构建工具:Apache Maven 3.6.3
- 组件版本:jcasbin 1.99.0(Maven Central 最新稳定版;1.100.0-snapshot.x 为快照,勿用于生产)
- 实验目录:
/root/casbin-demo(Maven 工程) - 执行用户:root
二、前置条件与依赖引入
只需 JDK 8+ 与网络可达。工程 pom.xml 加入:
<dependency>
<groupId>org.casbin</groupId>
<artifactId>jcasbin</artifactId>
<version>1.99.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.16</version>
</dependency>
说明:slf4j-simple 为可选日志实现,须用 2.x(与 jcasbin 内置 slf4j-api 2.0.16 配套)。编译并导出依赖:
cd /root/casbin-demo && mvn -q compile && mvn -q dependency:copy-dependencies -DoutputDirectory=target/dependency && ls target/dependency | wc -l
回显(实测):
13
jcasbin 带全套传递依赖(aviator 表达式引擎、commons-csv、ipaddress、slf4j 等 13 个 jar),手动部署时按 dependency:copy-dependencies 导出,不能只拷主 jar。
三、核心概念速览
| 概念 | 载体 | 一句话理解 |
|---|---|---|
| Model | model.conf | 授权逻辑:请求/策略/角色/效果/匹配器五段定义 |
| Policy | policy.csv(经 FileAdapter) | 授权规则数据,p 行规则、g 行角色关系 |
| Enforcer | Java 对象 | 决策入口:enforce(args...) -> boolean |
| Adapter | FileAdapter 等 | 策略的加载与回写通道 |
最小 ACL 三件套(本篇第五节会复用):
[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 && r.obj == p.obj && r.act == p.act
echo 'p, alice, data1, read
p, bob, data2, write' > policy.csv
Enforcer e = new Enforcer("model.conf", "policy.csv");
System.out.println(e.enforce("alice", "data1", "read")); // true
System.out.println(e.enforce("alice", "data1", "write")); // false
四、集成示例:一个类跑通六组模型
示例类 demo.AdvancedDemo 代码整体复制即可运行(配套 5 组模型文件见 4.2 节),给出全文、运行命令与逐段解读。
4.1 完整示例代码
package demo;
import org.casbin.jcasbin.main.Enforcer;
import org.casbin.jcasbin.util.BuiltInFunctions;
import java.util.HashMap;
import java.util.Map;
public class AdvancedDemo {
static void t(String label, boolean ok) {
System.out.println(label + " => " + ok);
}
static Map<String, Object> age(int v) {
Map<String, Object> m = new HashMap<>();
m.put("age", v);
return m;
}
public static void main(String[] args) {
System.out.println("== 0. BuiltInFunctions built-in functions ==");
System.out.println("keyMatch(\"/foo/bar\", \"/foo/*\") = " + BuiltInFunctions.keyMatch("/foo/bar", "/foo/*"));
System.out.println("keyMatch(\"/foobar\", \"/foo/*\") = " + BuiltInFunctions.keyMatch("/foobar", "/foo/*"));
System.out.println("keyMatch2(\"/api/2021/01\", \"/api/*\") = " + BuiltInFunctions.keyMatch2("/api/2021/01", "/api/*"));
System.out.println("keyMatch2(\"/api/2021/01\", \"/api/:id\") = " + BuiltInFunctions.keyMatch2("/api/2021/01", "/api/:id"));
System.out.println("keyMatch2(\"/api/2021\", \"/api/:id\") = " + BuiltInFunctions.keyMatch2("/api/2021", "/api/:id"));
System.out.println("globMatch(\"/foo/bar\", \"/foo/*\") = " + BuiltInFunctions.globMatch("/foo/bar", "/foo/*"));
System.out.println("regexMatch(\"foo123\", \"^foo\") = " + BuiltInFunctions.regexMatch("foo123", "^foo"));
System.out.println("ipMatch(\"192.168.1.1\", \"192.168.0.0/16\") = " + BuiltInFunctions.ipMatch("192.168.1.1", "192.168.0.0/16"));
System.out.println("ipMatch(\"192.168.1.1\", \"10.0.0.0/8\") = " + BuiltInFunctions.ipMatch("192.168.1.1", "10.0.0.0/8"));
System.out.println("== 1. RESTful (keyMatch2 + regexMatch) ==");
Enforcer e1 = new Enforcer("restful_model.conf", "restful_policy.csv");
t("alice, /api1/child1, GET", e1.enforce("alice", "/api1/child1", "GET"));
t("alice, /api1/child1, POST", e1.enforce("alice", "/api1/child1", "POST"));
t("alice, /api1/child1, DELETE", e1.enforce("alice", "/api1/child1", "DELETE"));
t("alice, /api2/resource1, GET", e1.enforce("alice", "/api2/resource1", "GET"));
t("alice, /api2/resource1, POST", e1.enforce("alice", "/api2/resource1", "POST"));
System.out.println("== 2. keyMatch model ==");
Enforcer e2 = new Enforcer("keymatch_model.conf", "keymatch_policy.csv");
t("alice, /data1/1, read", e2.enforce("alice", "/data1/1", "read"));
t("alice, /data1/1, write", e2.enforce("alice", "/data1/1", "write"));
t("alice, /data2/2, write", e2.enforce("alice", "/data2/2", "write"));
t("alice, /foobar, read", e2.enforce("alice", "/foobar", "read"));
System.out.println("== 3. RBAC with deny-override ==");
Enforcer e3 = new Enforcer("rbac_deny_model.conf", "rbac_deny_policy.csv");
t("alice, data1, read", e3.enforce("alice", "data1", "read"));
t("alice, data2, write (role allow + user deny)", e3.enforce("alice", "data2", "write"));
t("bob, data2, write", e3.enforce("bob", "data2", "write"));
System.out.println("== 4. priority (explicit) ==");
Enforcer e4 = new Enforcer("priority_model.conf", "priority_policy.csv");
t("alice, data1, read (deny@10 vs allow@20)", e4.enforce("alice", "data1", "read"));
t("bob, data2, read (allow@10 via group)", e4.enforce("bob", "data2", "read"));
t("alice, data2, read (no match)", e4.enforce("alice", "data2", "read"));
t("bob, data1, read (no match)", e4.enforce("bob", "data1", "read"));
System.out.println("== 5. ABAC (eval rule on Map subject) ==");
Enforcer e5 = new Enforcer("abac_model.conf", "abac_policy.csv");
t("{age:20}, data1, read", e5.enforce(age(20), "data1", "read"));
t("{age:17}, data1, read", e5.enforce(age(17), "data1", "read"));
t("{age:70}, data2, read", e5.enforce(age(70), "data2", "read"));
t("{age:30}, data2, read", e5.enforce(age(30), "data2", "read"));
}
}
代码要点:内置函数直接调 BuiltInFunctions 静态方法(1.99.0 的 Util 类没有 keyMatch 系列);ABAC 主题直接传 HashMap,eval 规则里的 r.sub.age 由 Aviator 自动按 Map 取值;每个 Enforcer 用独立模型文件,互不干扰。
4.2 配套模型与策略文件(放工程根目录)
# restful_model.conf 匹配器:m = r.sub == p.sub && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)
echo 'p, alice, /api1/*, GET|POST
p, alice, /api2/:resource, GET' > restful_policy.csv
# keymatch_model.conf 匹配器:m = r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)
echo 'p, alice, /data1/*, read
p, alice, /data2/*, write' > keymatch_policy.csv
# rbac_deny_model.conf:p = sub, obj, act, eft;e = some(where (p.eft == allow)) && !some(where (p.eft == deny));m 用 g(r.sub, p.sub)
echo 'p, alice, data1, read, allow
p, data2_admin, data2, write, allow
g, alice, data2_admin
p, alice, data2, write, deny' > rbac_deny_policy.csv
# priority_model.conf:p = priority, sub, obj, act, eft;[priority_definition] p = p;e = priority(p.eft) || deny;m 用 g(r.sub, p.sub)
echo 'p, 10, data1_deny_group, data1, read, deny
p, 10, data2_allow_group, data2, read, allow
p, 20, alice, data1, read, allow
p, 21, bob, data2, read, allow
g, alice, data1_deny_group
g, bob, data2_allow_group' > priority_policy.csv
# abac_model.conf:p = sub_rule, obj, act;m = eval(p.sub_rule) && r.obj == p.obj && r.act == p.act
echo 'p, r.sub.age > 18, data1, read
p, r.sub.age < 60, data2, read' > abac_policy.csv
注意 deny 与 priority 模型的
p定义都必须显式含eft列——jcasbin 1.99.0 不支持 Go casbin 的隐式 eft(实测多一列即报invalid policy size)。
4.3 编译与运行(完整实测回显)
cd /root/casbin-demo && mvn -q exec:java -Dexec.mainClass=demo.AdvancedDemo
回显(实测):
== 0. BuiltInFunctions built-in functions ==
keyMatch("/foo/bar", "/foo/*") = true
keyMatch("/foobar", "/foo/*") = false
keyMatch2("/api/2021/01", "/api/*") = true
keyMatch2("/api/2021/01", "/api/:id") = false
keyMatch2("/api/2021", "/api/:id") = true
globMatch("/foo/bar", "/foo/*") = true
regexMatch("foo123", "^foo") = true
ipMatch("192.168.1.1", "192.168.0.0/16") = true
ipMatch("192.168.1.1", "10.0.0.0/8") = false
== 1. RESTful (keyMatch2 + regexMatch) ==
alice, /api1/child1, GET => true
alice, /api1/child1, POST => true
alice, /api1/child1, DELETE => false
alice, /api2/resource1, GET => true
alice, /api2/resource1, POST => false
== 2. keyMatch model ==
alice, /data1/1, read => true
alice, /data1/1, write => false
alice, /data2/2, write => true
alice, /foobar, read => false
== 3. RBAC with deny-override ==
alice, data1, read => true
alice, data2, write (role allow + user deny) => false
bob, data2, write => false
== 4. priority (explicit) ==
alice, data1, read (deny@10 vs allow@20) => false
bob, data2, read (allow@10 via group) => true
alice, data2, read (no match) => false
bob, data1, read (no match) => false
== 5. ABAC (eval rule on Map subject) ==
{age:20}, data1, read => true
{age:17}, data1, read => false
{age:70}, data2, read => false
{age:30}, data2, read => true
4.4 逐段解读
- [0] 内置函数:keyMatch 的
*只做前缀通配(/foobar不匹配);keyMatch2 的:id精确吃一段、*通配剩余全部——RESTful 路由按需选择。 - [1] RESTful:策略 act 列写
GET|POST被当作正则;/api1/*放行子路径全部请求,DELETE 不在正则内故拒绝。 - [2] keyMatch:
/data1/*覆盖/data1/1但不覆盖/foobar,act 精确匹配 read/write。 - [3] deny-override:alice 经
g继承 data2_admin 的 write allow,但自身显式 deny 一票否决 → false;deny 必须显式在 eft 列。 - [4] 优先级:priority 数值小者优先——组规则 deny@10 压过用户规则 allow@20;两组之间互不影响(未命中的请求仍为 false)。
- [5] ABAC:规则写在策略里(
r.sub.age > 18),运行期用 eval 求值;同一个 20 岁主题读 data1 放行、读 data2 也要满足< 60,边界值 17/70 均被拒绝。
💡 技术拓展:模型选型对照——
场景 推荐模型 关键参数 精确授权(资源数可控) ACL r.sub == p.sub角色化授权 RBAC g(r.sub, p.sub)+g, user, role多租户 RBAC with domains g = _, _, _+ dom 列"例外拒绝"审计要求 deny-override 显式 eft 列 + 组合 effect 规则冲突需确定性裁决 显式优先级 priority 列 + priority(p.eft) || deny按属性动态判定 ABAC eval(p.sub_rule)
五、常用 API 速查
5.1 ACL 链路实测(demo.CasbinDemo)
alice, data1, read => true
alice, data1, write => false
[after addPolicy] alice, data2, write => true
removeFilteredPolicy(0, alice, data2) => true
[after remove] alice, data2, write => false
savePolicy done
5.2 Management / RBAC API(jshell 实测)
addPolicy(cathy,data1,read) => true
hasPolicy(cathy,data1,read) => true
updatePolicy([cathy,data1,read]->[cathy,data1,write]) => true
getFilteredPolicy(1,data1) => [[alice, data1, read], [cathy, data1, write]]
addRoleForUser(alice,data2_admin) => true
getRolesForUser(alice) => [data2_admin]
getImplicitRolesForUser(alice) => [data2_admin, senior_admin] # 含继承链
getImplicitPermissionsForUser(alice) => [[alice, data1, read], [data2_admin, data2, read], [data2_admin, data2, write]]
getImplicitUsersForRole(senior_admin) => [data2_admin, alice]
deleteRolesForUser(alice) => true
方法族规律:has/add/update/remove + Policy 管规则行;getRolesForUser / getUsersForRole / addRoleForUser / deleteRole(s)ForUser 管角色;带 Implicit 前缀的会展开继承链与组权限;InDomain 后缀用于 domain 模型(如 getRolesForUserInDomain(alice, domain1) 实测返回 [admin])。
六、注意事项
- deny/priority 模型必须显式写 eft 列,策略行数与定义严格一致,否则
invalid policy size。 - 匹配器用
g(...)时模型必须有[role_definition]段,否则Function not found: g。 - 内置函数在
BuiltInFunctions(静态调用)与util.function.*Func(模型注册用),不在Util类。 - 默认 autoSave 开启:addPolicy/removePolicy 即时写回策略文件;只改内存时先
enableAutoSave(false)。 - FileAdapter 按工作目录相对路径读写且 savePolicy 要求文件已存在;示例从工程根目录运行。
- slf4j 实现绑 2.x(1.7.x 会出兼容告警);需要静默换 slf4j-nop。
七、参考链接
- jcasbin 官方仓库与示例:https://github.com/casbin/jcasbin
- Casbin 官方文档(get-started):https://casbin.org/zh/docs/get-started
- 内置函数一览:https://casbin.org/zh/docs/builtin-functions
- Management API 与 RBAC API:https://casbin.org/zh/docs/management-api
- Maven 中央仓库 jcasbin:https://mvnrepository.com/artifact/org.casbin/jcasbin