注册

​从RBAC到ABAC的进阶之路:基于jCasbin实现无侵入的SpringBoot权限校验​

一、前言:当权限判断写满业务代码


几乎所有企业系统,都逃不过“权限”这道关。

从“谁能看”、“谁能改”到“谁能审批”,权限逻辑贯穿了业务的方方面面。


起初,大多数项目使用最常见的 RBAC(基于角色的访问控制) 模型


if (user.hasRole("admin")) {
documentService.update(doc);
}

逻辑简单、上手快,看似能解决 80% 的问题。

但随着业务复杂度上升,RBAC 很快会失控。


比如你可能遇到以下需求 👇



  • “文档的作者可以编辑自己的文档”;
  • “同部门的经理也可以编辑该文档”;
  • “外部合作方仅能查看共享文档”;
  • “项目归档后,所有人都只读”。

这些场景无法用“角色”简单定义,

于是权限判断开始蔓延在业务代码各处,像这样:


if (user.getId().equals(doc.getOwnerId()) 
|| (user.getDept().equals(doc.getDept()) && user.isManager())) {
// 编辑文档
} else {
throw new AccessDeniedException("无权限");
}

时间久了,这些判断像杂草一样蔓延。

权限逻辑与业务逻辑纠缠不清,修改一处可能引发连锁反应。

可维护性、可测试性、可演化性统统崩盘。


二、RBAC 的天花板:角色无法描述现实世界


RBAC 的问题在于:它过于静态

“角色”可以描述一类人,但描述不了上下文。


举个例子:



研发经理能编辑本部门的文档,但不能编辑市场部的。



在 RBAC 下,你只能再创建新角色:

研发经理市场经理项目经理……

角色越来越多,最终爆炸。


而现实世界的权限,往往与“属性”有关:



  • 用户的部门
  • 资源的拥有者
  • 操作发生的时间 / 状态

这些动态因素,是 RBAC 无法覆盖的。

于是我们需要一个更灵活的模型 —— ABAC


三、ABAC:基于属性的访问控制


ABAC(Attribute-Based Access Control) 的核心理念是:



授权决策 = 函数(主体属性、资源属性、操作属性、环境属性)



概念含义示例
Subject(主体)谁在访问用户A,部门=研发部
Object(资源)访问什么文档1,ownerId=A,部门=研发部
Action(操作)做什么edit / read / delete
Policy(策略)允许条件user.dept == doc.dept && act == "edit"

一句话总结:



ABAC 不关心用户是谁,而关心“用户和资源具有什么属性”。



举例说明:



“用户可以编辑自己部门的文档,或自己创建的文档。”



简单、直观、灵活。


四、引入 JCasbin:让授权逻辑从代码中消失


JCasbin(github.com/casbin/jcas…) 是一个优秀的 Java 权限引擎,支持多种模型(RBAC、ABAC)。


它最大的价值在于:

把授权逻辑从代码中抽离,让代码只负责执行业务。


在 JCasbin 中,我们通过定义:



  • 模型文件(model) :规则框架;
  • 策略文件(policy) :具体规则。

然后由 Casbin 引擎来执行判断。


五、核心实现:几行配置搞定动态权限


模型文件 model.conf


[request_definition]
r = sub, obj, act

[policy_definition]
p = sub_rule, obj_rule, act

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

[matchers]
m = eval(p.sub_rule) && eval(p.obj_rule) && r.act == p.act

策略文件 policy.csv


p, r.sub.dept == r.obj.dept, true, edit
p, r.sub.id == r.obj.ownerId, true, edit
p, true, true, read

解释:



  • 同部门可编辑;
  • 作者可编辑;
  • 所有人可阅读。

在代码中调用


Enforcer enforcer = new Enforcer("model.conf", "policy.csv");

User user = new User("u1", "研发部");
Document doc = new Document("d1", "研发部", "u1");

boolean canEdit = enforcer.enforce(user, doc, "edit");
System.out.println("是否有编辑权限:" + canEdit);

输出:


是否有编辑权限:true

无需任何 if-else,逻辑全在外部配置中定义

业务代码只需调用 Enforcer,简单又优雅。




六、在 Spring Boot 中实现“无感校验”


实际项目中,我们希望权限校验能“自动触发”,

这可以通过 注解 + AOP 切面 的方式实现。


定义注解


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckPermission {
String action();
}

编写切面


@Aspect
@Component
public class PermissionAspect {

@Autowired
private Enforcer enforcer;

@Before("@annotation(checkPermission)")
public void checkAuth(JoinPoint jp, CheckPermission checkPermission) {
Object user = getCurrentUser();
Object resource = getRequestResource(jp);
String action = checkPermission.action();

if (!enforcer.enforce(user, resource, action)) {
throw new AccessDeniedException("无权限执行操作:" + action);
}
}
}

在业务代码中使用


@CheckPermission(action = "edit")
@PostMapping("/doc/edit")
public void editDoc(@RequestBody Document doc) {
documentService.update(doc);
}

✅ 授权逻辑彻底从业务中解耦,权限统一由 Casbin 引擎处理。


七、策略动态化与分布式支持


在生产环境中,权限策略通常存储在数据库中,而非文件。

JCasbin 支持多种扩展方式:


JDBCAdapter adapter = new JDBCAdapter(dataSource);
Enforcer enforcer = new Enforcer("model.conf", adapter);

支持特性包括:



  • 💽 MySQL / PostgreSQL 等持久化;
  • 🔄 Redis Watcher 实现多节点策略热更新;
  • ⚡ SyncedEnforcer 支持高并发一致性。

这样修改权限规则就无需重新部署代码,权限即改即生效


八、总结


引入 JCasbin 后,项目结构会发生显著变化👇


优势描述
逻辑解耦授权逻辑完全从业务代码中剥离
灵活配置权限规则动态可改、可热更新
可扩展可根据属性定义复杂条件
统一决策所有权限判断走同一引擎
可测试策略可单测,无需跑整套业务流程

最重要的是:新增规则无需改代码

只要在策略表里加一条记录,就能实现全新的授权逻辑。


权限系统的复杂,不在于“能不能判断”,

而在于——“判断逻辑放在哪儿”。


当项目越做越大,你会发现:



真正的架构能力,不是多写逻辑,而是让逻辑有边界。



JCasbin 给了我们一个极好的解法:

一个统一的决策引擎,让权限系统既灵活又有秩序。


它不是银弹,但能让你在权限处理上的代码更纯净、系统扩展性更好。


github.com/yuboon/java…


作者:风象南
来源:juejin.cn/post/7558094123812536361

0 个评论

要回复文章请先登录注册