一、 官方示例
首先,官網給出了簡單了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
