jcasbin 1.99.0 快速入门与集成示例

jcasbin 1.99.0 快速入门:依赖引入与核心概念,一个类跑通内置匹配函数、RESTful、keyMatch、RBAC deny-override、显式优先级、ABAC 五组模型,Management/RBAC API 速查,全程实测。

Apache CasbinCasbin2026/10/13

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。

三、核心概念速览

概念载体一句话理解
Modelmodel.conf授权逻辑:请求/策略/角色/效果/匹配器五段定义
Policypolicy.csv(经 FileAdapter)授权规则数据,p 行规则、g 行角色关系
EnforcerJava 对象决策入口:enforce(args...) -> boolean
AdapterFileAdapter 等策略的加载与回写通道

最小 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 均被拒绝。

💡 技术拓展:模型选型对照——

场景推荐模型关键参数
精确授权(资源数可控)ACLr.sub == p.sub
角色化授权RBACg(r.sub, p.sub) + g, user, role
多租户RBAC with domainsg = _, _, _ + dom 列
"例外拒绝"审计要求deny-override显式 eft 列 + 组合 effect
规则冲突需确定性裁决显式优先级priority 列 + priority(p.eft) || deny
按属性动态判定ABACeval(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。

七、参考链接

  1. jcasbin 官方仓库与示例:https://github.com/casbin/jcasbin
  2. Casbin 官方文档(get-started):https://casbin.org/zh/docs/get-started
  3. 内置函数一览:https://casbin.org/zh/docs/builtin-functions
  4. Management API 与 RBAC API:https://casbin.org/zh/docs/management-api
  5. Maven 中央仓库 jcasbin:https://mvnrepository.com/artifact/org.casbin/jcasbin