spring security在activiti中的应用 #
1.1 流程部署 #
Spring Security主要在以下几个场景中起作用:
- 用户认证与授权 通过UserDetailsService实现用户认证,如ApplicationConfiguration类中的myUserDetailsService()方法 使用SecurityManager进行安全管理,如LocalSpringSecurityManager类 通过SecurityContextPrincipalProvider获取当前认证用户信息
- 流程安全策略管理 通过ProcessSecurityPoliciesManager实现流程定义和实例的访问控制 使用SecurityPoliciesRestrictionApplier限制流程查询范围
- 方法级安全控制 通过ActivitiMethodSecurityAutoConfiguration启用全局方法安全 支持@PreAuthorize、@Secured等注解进行方法级权限控制
- 用户角色与权限管理 通过PrincipalRolesProvider和PrincipalGroupsProvider管理用户角色和组 使用GrantedAuthoritiesResolver解析用户权限
- REST API安全 通过EndpointAutoConfiguration配置Actuator端点安全 保护流程引擎相关API的访问
- 测试环境安全模拟 使用SecurityUtil类在测试中模拟用户登录 通过@WithMockUser等注解进行测试用户模拟
- 安全策略配置 通过SecurityPoliciesProperties配置安全策略 支持基于用户、组、服务名称和流程key的访问控制 这些安全机制共同构成了工程的安全体系,确保系统资源和业务流程的访问受到适当控制。
1.1.1 用户认证与授权 #
@PreAuthorize("hasRole('ACTIVITI_USER')")
public class TaskRuntimeImpl implements TaskRuntime {
graph TD
A[调用TaskRuntimeImpl方法] --> B{Spring Security拦截器}
B -->|是| C[获取当前认证信息]
B -->|否| D[继续执行方法]
C --> E{是否已认证?}
E -->|是| F{是否有ACTIVITI_USER角色?}
E -->|否| G[抛出AccessDeniedException]
F -->|是| H[继续执行方法]
F -->|否| I[抛出AccessDeniedException]
详细执行流程:
- 方法调用:当调用TaskRuntimeImpl中的任何方法时,Spring Security的AOP拦截器会首先执行。
- 认证检查: 通过SecurityContextHolder.getContext().getAuthentication()获取当前认证信息 检查用户是否已认证(authentication.isAuthenticated())
- 角色验证: 从Authentication对象中获取用户权限(getAuthorities()) 检查权限集合中是否包含ROLE_ACTIVITI_USER
- 决策执行: 如果用户拥有ACTIVITI_USER角色,允许方法继续执行 如果用户没有该角色,抛出AccessDeniedException异常
- 异常处理: Spring Security会将AccessDeniedException转换为HTTP 403 Forbidden响应(在Web应用中)
1.1.1.1 在Spring Security中,用户认证与授权后的数据过滤主要通过以下几种方式实现 #
- 方法级安全控制:
- 使用@PreAuthorize注解进行方法调用前的权限验证
- 示例:@PreAuthorize(“hasRole(‘ACTIVITI_USER’)")确保只有拥有ACTIVITI_USER角色的用户才能访问方法
- 查询级数据过滤:
- 在数据库查询时根据当前用户信息进行过滤
@Override
public Page<Task> tasks(Pageable pageable) {
String authenticatedUserId = securityManager.getAuthenticatedUserId();
if (authenticatedUserId != null && !authenticatedUserId.isEmpty()) {
List<String> userGroups = securityManager.getAuthenticatedUserGroups();
return tasks(pageable,
TaskPayloadBuilder.tasks().withAssignee(authenticatedUserId).withGroups(userGroups).build());
}
throw new IllegalStateException("You need an authenticated user to perform a task query");
}
- 业务逻辑级过滤:
- 在业务逻辑中根据用户权限进行数据过滤
private List<IdentityLink> getIdentityLinks(String taskId) {
String authenticatedUserId = securityManager.getAuthenticatedUserId();
if (authenticatedUserId != null && !authenticatedUserId.isEmpty()) {
List<String> userRoles = securityManager.getAuthenticatedUserRoles();
List<String> userGroups = securityManager.getAuthenticatedUserGroups();
org.activiti.engine.task.Task internalTask = taskService.createTaskQuery()
.taskCandidateOrAssigned(authenticatedUserId, userGroups)
.taskId(taskId)
.singleResult();
if (internalTask == null) {
throw new NotFoundException("Unable to find task for the given id: " + taskId);
}
return taskService.getIdentityLinksForTask(taskId);
}
throw new IllegalStateException("There is no authenticated user, we need a user authenticated to find tasks");
}
- 数据访问层过滤:
- 在数据访问层根据用户权限进行数据过滤
- 使用Spring Data JPA的@Query注解或Specification进行过滤
- 视图层过滤:
- 在视图层根据用户权限显示或隐藏某些数据
- 使用Thymeleaf或JSP等模板引擎的security标签进行控制
1.1.2 流程安全策略管理
graph TD
A[用户请求] --> B{是否认证?}
B -->|是| C[获取安全上下文]
B -->|否| D[重定向到登录页]
C --> E{是否有权限?}
E -->|是| F[执行请求]
E -->|否| G[返回403错误]
在Activiti工程中,流程安全策略主要通过ProcessSecurityPoliciesManager接口及其实现类来管理。以下是核心实现和具体使用逻辑:
- 流程定义访问: 用户请求访问流程定义时,首先检查canRead权限 如果用户没有读取权限,抛出ActivitiObjectNotFoundException
- 流程实例访问: 用户请求访问流程实例时,检查canRead权限 同时验证用户是否是流程启动者或任务参与者 如果权限不足,抛出ActivitiObjectNotFoundException
- 流程操作权限: 用户尝试启动、挂起、恢复、删除流程时,检查canWrite权限 如果用户没有写权限,抛出ActivitiForbiddenException
- 查询过滤: 在查询流程定义和实例时,通过restrictProcessDefQuery和restrictProcessInstQuery方法过滤结果 只返回用户有权限访问的流程定义和实例