一、 官方示例
首先,官网给出了简单了demo 示例:https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-task-example/src/main/java/org/activiti/examples/DemoApplicationConfiguration.java#L26
建议大家将完成的demo下载下来仔细看下。下面说下其主要思路
1 SecurityUtil
@Component public class SecurityUtil { @Autowired private UserDetailsService userDetailsService; public void logInAs(String username) { UserDetails user = userDetailsService.loadUserByUsername(username); if (user == null) { throw new IllegalStateException("User " + username + " doesn't exist, please provide a valid user"); } SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() { @Override public Collection<? extends GrantedAuthority> getAuthorities() { return user.getAuthorities(); } @Override public Object getCredentials() { return user.getPassword(); } @Override public Object getDetails() { return user; } @Override public Object getPrincipal() { return user; } @Override public boolean isAuthenticated() { return true; } @Override public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { } @Override public String getName() { return user.getUsername(); } })); org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username
此类关键代码 一
SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() { @Override public Collection<? extends GrantedAuthority> getAuthorities() { return user.getAuthorities();
认证上下文中设置了Authentication 对象,此对象返回了认证者的权限Authorities集合,后面会拿这个结果做判定,如果没有 ROLE_ACTIVITI_USER 将会抛出异常,提示无法访问。
此类关键代码 二
org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
往 Authentication 上下文中设置了登录者,注意了,此处的Authentication 和前面Spring SecurityContextHolder 中的不一样,二者包名不一样。activiti 中 Authentication中的线程变量用于后面的用户信息获取。比如设置发起人,当前用户的任务等。
2 配置类
@Bean public UserDetailsService myUserDetailsService() { InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager(); String[][] usersGroupsAndRoles = { {"system", "password", "ROLE_ACTIVITI_USER"}, {"admin", "password", "ROLE_ACTIVITI_ADMIN"}, }; for (String[] user : usersGroupsAndRoles) { List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length)); logger.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]"); inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]), authoritiesStrings.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList()))); } return inMemoryUserDetailsManager
注入了 InMemoryUserDetailsManager 这个用户服务,新建了两个用户 system ,admin,具备相应的角色,请注意 ROLE_ACTIVITI_ADMIN 的用户将无法访问调用相关的api 。
二、 接入自定义身份
根据以上两点,我们就可以改造,并接入自己的身份系统。
1 用户
1 编写 ActivitiUserDetailsManager
UserDetailsService 将会注入我们自己的身份服务ActivitiUserDetailsManager。其实现我们仿造InMemoryUserDetailsManager 类即可,关键代码是重写 如下方法:
@Override public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException { BizUserEntity bizUser = userService.getBizUserById(userId); return new User(bizUser.getName(), "", Collections.singletonList(new SimpleGrantedAuthority("ROLE_ACTIVITI_USER"))); }
其中
BizUserEntity bizUser = userService.getBizUserById(userId);
这个是自己业务中身份服务查询方法,并且将此对象转换为如下用户
org.springframework.security.core.userdetails.User;
并且具备ROLE_ACTIVITI_USER 这个角色。
2 配置类
和demo一样,注入UserDetailsService 即可。
@Bean public UserDetailsService activitiUserDetailsService() { return new ActivitiUserDetailsManager(userService,groupManager); }
此次的参数 userService,groupManager 就是自己业务系统的用户/组管理服务,我这里是通过构造器传入进去的,你也可以通过其他方式实现注入。说白了就是ActivitiUserDetailsManager 这个类呢对你自己的userService 又作了一层简单的包装,这样可以做到和工作流的用户服务解耦。
2 用户组
等等,以上的案例只是说明了用户查询,但是用户组呢?其实activiti 7 中,有默认的实现
org.activiti.core.common.spring.identity.ActivitiUserGroupManagerImpl
其中,有两个最重要的方法:获取用户组,获取用户角色
public List<String> getUserGroups(String username) { return (List)this.userDetailsService.loadUserByUsername(username).getAuthorities().stream().filter((a) -> { return a.getAuthority().startsWith("GROUP_"); }).map((a) -> { return a.getAuthority().substring(6); }).collect(Collectors.toList()); } public List<String> getUserRoles(String username) { return (List)this.userDetailsService.loadUserByUsername(username).getAuthorities().stream().filter((a) -> { return a.getAuthority().startsWith("ROLE_"); }).map((a) -> { return a.getAuthority().substring(5); }).collect(Collectors.toList()); }
这里其实也是通过userDetailsService 这个用户查询来做的,并且 是通过前缀匹配去查询 ROLE_ , GROUP_ 的字符串,到这里也就不难理解demo中如下代码了
{"system", "password", "ROLE_ACTIVITI_USER"}, {"admin", "password", "ROLE_ACTIVITI_ADMIN"}
因此,仿造ActivitiUserGroupManagerImpl 的实现,新建 ActivitiGroupManagerImpl 实现
org.activiti.api.runtime.shared.identity.UserGroupManager
重写相关的方法即可。值得注意的一点是,需要加上@Primary这个注解
因为 默认的 ActivitiUserGroupManagerImpl 也是自动交给spring初始化了,加上@Primary 就是告诉spring 当有多个 实现类shi,用ActivitiGroupManagerImpl 这个类。
到此,就完全接入自己的身份系统了,包含用户和用户组的能力。对于 activiti7 以下的版本,实现会有很大的差异,但是官方文档给出了解决方案,参见如下:
https://www.activiti.org/userguide/index.html#advanced.custom.session.manager
https://my.oschina.net/woniuyi/blog/4714756