1. 为什么你的Camunda集成总被拦截?一个真实项目的权限冲突剖析
很多朋友在SpringBoot项目里集成Camunda时,都会遇到一个让人头疼的问题:明明按照官方文档配置好了,启动项目后访问http://localhost:8080/camunda/app,结果要么是404,要么直接跳到了自己系统的登录页。我刚开始做集成时也踩过这个坑,折腾了大半天才发现问题根源。
其实这个问题的本质很简单:你的业务系统有自己的权限拦截器,而Camunda的Web界面和REST接口也被这个拦截器给拦住了。想象一下,你家的门卫不认识Camunda这位客人,自然就不让他进门。Camunda默认是没有任何权限控制的,所有接口都是开放的,但你的Spring Security或者Shiro配置里,通常会有类似/**这样的拦截规则,把/camunda/**和/engine-rest/**这些路径也一并拦截了。
我遇到过最典型的情况是,团队用了某个开源框架作为基础,比如小诺(xiaonuo)或者若依(RuoYi),这些框架自带的权限拦截非常完善,但也非常“霸道”。它们会检查每个请求的Session里有没有登录用户,没有就直接重定向到登录页。而Camunda的Web界面初次访问时,显然没有你业务系统的Session,于是就被无情地赶走了。
更麻烦的是,即使你放行了Camunda的路径,另一个问题又来了:Camunda自己变成了“裸奔”状态。因为你只是让门卫放行了这位客人,但没给这位客人自己家的门加锁。任何知道Camunda控制台地址的人都能直接访问,查看所有流程、操作所有任务,这在生产环境是绝对不允许的。所以,我们需要做两件事:第一,让我们系统的门卫对Camunda“网开一面”;第二,给Camunda这位客人自己装上独立的安全锁。下面我们就一步步来解决。
2. 基础集成:三分钟让Camunda在SpringBoot里跑起来
我们先从最简单的开始,确保Camunda能正常集成到你的SpringBoot项目里。这里我以Camunda 7.19.0版本为例(写这篇文章时的稳定版),但原理对7.x系列都通用。
首先,在你的pom.xml里加入这几个核心依赖。别一股脑全加,看清楚每个是干嘛的:
<!-- Camunda核心启动器,必须的 -->
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter</artifactId>
<version>7.19.0</version>
</dependency>
<!-- 如果你需要Camunda自带的Web管理界面(Cockpit、Tasklist等),加上这个 -->
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId>
<version>7.19.0</version>
</dependency>
<!-- 如果你需要通过REST API操作引擎,加上这个 -->
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter-rest</artifactId>
<version>7.19.0</version>
</dependency>
<!-- 用于JSON/XML等数据格式处理,建议加上 -->
<dependency>
<groupId>org.camunda.bpm</groupId>
<artifactId>camunda-engine-plugin-spin</artifactId>
<version>7.19.0</version>
</dependency>
<dependency>
<groupId>org.camunda.spin</groupId>
<artifactId>camunda-spin-dataformat-json-jackson</artifactId>
<version>1.18.0</version>
</dependency>
加完依赖,在application.yml里做最基础的配置:
camunda:
bpm:
admin-user:
id: admin # Camunda默认管理员账号
password: admin # 生产环境一定要改!
filter:
create: All # 非常重要!这行让SpringBoot自动创建Camunda的过滤器
spring:
datasource:
url: jdbc:mysql://localhost:3306/camunda?useUnicode=true&characterEncoding=utf8
username: your_db_user
password: your_db_password
driver-class-name: com.mysql.cj.jdbc.Driver
配置完启动项目,如果没意外,Camunda会自动创建几十张表在你的数据库里。这时候你访问http://localhost:8080/camunda/app,应该能看到Camunda的登录界面了。用上面配置的admin/admin就能登录进去。但注意,这时候Camunda是“裸奔”的,没有任何权限控制,谁拿到这个地址都能登录操作,这显然不行。
提示:如果你用的是内存数据库比如H2,Camunda也能跑起来,但生产环境强烈建议用MySQL、PostgreSQL这类持久化数据库。第一次启动时Camunda会自动建表,这个过程可能需要几十秒,耐心等一下。
3. 解决第一道门禁:让你的业务系统放行Camunda
现在来解决第一个问题——你业务系统的权限拦截。不同的框架配置方式不同,我以最常见的Spring Security和基于拦截器的权限框架为例,告诉你该怎么放行。
如果你用的是Spring Security,配置相对简单。在你的Security配置类(通常叫SecurityConfig)里,找到configure(HttpSecurity http)方法,添加对Camunda路径的放行:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
// 放行Camunda的静态资源和API接口
.antMatchers("/camunda/app/**", "/camunda/api/**", "/camunda/lib/**").permitAll()
.antMatchers("/engine-rest/**").permitAll() // 放行REST API
.antMatchers("/forms/**").permitAll() // 放行表单引擎
// 其他业务路径按需配置
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
}
如果你用的是类似小诺(xiaonuo)这种基于拦截器的权限框架,情况稍微复杂点。这类框架通常有一个核心的拦截器配置,你需要找到配置拦截规则的地方。比如在小诺框架里,你可能会在WebMvcConfig或者类似的配置类里看到这样的代码:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 原有的拦截器配置
registry.addInterceptor(authInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login", "/logout", "/captcha");
// 你需要在这里加上对Camunda路径的排除
registry.addInterceptor(authInterceptor())
.addPathPatterns("/**")
.excludePathPatterns(
"/login", "/logout", "/captcha",
"/camunda/**", // 放行所有Camunda Web路径
"/engine-rest/**", // 放行REST API
"/forms/**" // 放行表单相关
);
}
}
还有一种情况,你的拦截器可能是通过Filter实现的。这时候你需要修改web.xml或者FilterRegistrationBean的配置。我遇到过最隐蔽的一种情况是:框架的权限判断逻辑写在了Filter里,而且判断请求头Authorization的方式比较粗暴。比如有些框架看到Authorization头就认为是自己的Token,但Camunda的REST接口用的是Basic认证,格式是Basic Base64编码,这就会导致冲突。这时候你可能需要修改拦截器的逻辑,让它能区分这两种认证方式。
做完这一步,重启项目,再次访问http://localhost:8080/camunda/app,应该能正常看到Camunda的登录页了。但别高兴太早,这只是过了第一关。
4. Camunda原生权限体系深度解析:不只是用户名密码那么简单
很多人以为Camunda的权限就是登录时的用户名密码验证,其实远不止这么简单。Camunda有一套完整的、基于资源的权限体系,理解这个体系是做好权限集成的关键。
Camunda把权限分成了两个层面:认证(Authentication)和授权(Authorization)。认证解决“你是谁”的问题,就是验证用户名密码对不对。授权解决“你能干什么”的问题,就是验证这个用户有没有权限执行某个操作。
在数据库层面,Camunda用几张核心表来管理权限:
ACT_ID_USER:用户表,存用户名、密码(加密过的)、邮箱等ACT_ID_GROUP:用户组表,可以理解成角色ACT_ID_MEMBERSHIP:用户和组的关联表ACT_RU_AUTHORIZATION:运行时授权表,这里记录了谁对什么资源有什么权限
Camunda内置了多种资源类型,每种都有对应的数字编码。我从源码里扒出来的完整列表是这样的:
| 资源类型 | 编码 | 说明 |
|---|---|---|
| APPLICATION | 0 | 应用权限,比如能否访问Cockpit、Tasklist |
| USER | 1 | 用户管理权限 |
| GROUP | 2 | 用户组管理权限 |
| GROUP_MEMBERSHIP | 3 | 用户组成员关系权限 |
| AUTHORIZATION | 4 | 授权管理权限 |
| FILTER | 5 | 过滤器权限 |
| PROCESS_DEFINITION | 6 | 流程定义权限 |
| TASK | 7 | 任务权限(最重要的之一) |
| PROCESS_INSTANCE | 8 | 流程实例权限 |
| DEPLOYMENT | 9 | 部署权限 |
| TENANT | 11 | 租户权限 |
我举个例子你就明白了。假设我们新建一个用户user2,只给他分配了访问Cockpit应用的权限(APPLICATION,编码0)。这时候user2登录后,能看到Cockpit界面,但里面是空的,看不到任何任务。为什么?因为他没有TASK(编码7)的READ权限。即使有一个任务明确分配给他处理,他也看不到。
你需要通过Camunda的管理界面或者API,给user2添加对TASK资源的READ权限,他才能看到自己的待办任务。如果想让他看到所有任务(比如主管查看下属任务),还需要给他添加对FILTER(编码5)的READ权限。这种细粒度的控制非常灵活,但也意味着配置工作会比较繁琐。
注意:Camunda的权限验证是“白名单”机制。默认情况下,新建的用户没有任何权限(除了管理员)。你必须显式地给他授权,他才能执行相应操作。这点和很多系统“默认有基础权限”的设计思路不同。
5. 实战开发:编写自定义的Camunda认证过滤器
现在进入核心部分——如何把我们业务系统的用户体系,和Camunda的权限体系打通。Camunda提供了ProcessEngineAuthenticationFilter这个过滤器来处理认证,我们需要实现自己的AuthenticationProvider。
先创建一个配置类,注册这个过滤器:
@Configuration
public class CamundaSecurityFilterConfig {
@Bean
public FilterRegistrationBean<ProcessEngineAuthenticationFilter> processEngineAuthenticationFilter() {
FilterRegistrationBean<ProcessEngineAuthenticationFilter> registration = new FilterRegistrationBean<>();
registration.setName("camunda-auth");
registration.setFilter(new ProcessEngineAuthenticationFilter());
// 关键:指定我们自定义的认证提供器
registration.addInitParameter("authentication-provider",
"com.yourcompany.camunda.provider.CustomAuthenticationProvider");
// 只拦截Camunda的路径,不要影响业务系统
registration.addUrlPatterns("/camunda/*", "/engine-rest/*");
registration.setOrder(1); // 设置合适的顺序,确保在业务系统过滤器之后
return registration;
}
}
重点是这个CustomAuthenticationProvider,它需要实现Camunda的AuthenticationProvider接口。我写了一个比较完整的版本,支持两种认证方式:
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
// Basic认证的前缀,Camunda REST客户端会用这种格式
private static final String BASIC_AUTH_PREFIX = "Basic ";
// Bearer Token前缀,我们业务系统通常用这种


915

被折叠的 条评论
为什么被折叠?



