1, Official example
First, the official website gives a simple demo example: https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-task-example/src/main/java/org/activiti/examples/DemoApplicationConfiguration.java#L26
I suggest you download the completed demo and have a closer look. Here are the main ideas
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
Such key code I
SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() { @Override public Collection<? extends GrantedAuthority> getAuthorities() { return user.getAuthorities();
The Authentication object is set in the Authentication context. This object returns the permission Authorities collection of the authenticator. This result will be used for judgment later. If there is no ROLE_ACTIVITI_USER} will throw an exception indicating that it is inaccessible.
Such key code 2
org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
Set the login in the Authentication context. Note that the Authentication here is different from that in the previous Spring SecurityContextHolder, and their package names are different. The thread variable in Authentication in activiti is used to obtain user information later. For example, set the initiator and the task of the current user.
2 configuration class
@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
Injects the user service InMemoryUserDetailsManager, and creates two new users, system, admin, with corresponding roles. Please note the ROLE_ACTIVITI_ADMIN users will not be able to access and invoke the relevant APIs.
2, Access Custom identity
According to the above two points, we can transform and access our own identity system.
1 user
1) ActivitiUserDetailsManager
UserDetailsService will inject our own identity service ActivitiUserDetailsManager. We can imitate the InMemoryUserDetailsManager class for its implementation. The key code is to rewrite the following methods:
@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"))); }
among
BizUserEntity bizUser = userService.getBizUserById(userId);
This is the identity service query method in your own business, and this object is transformed into the following users
org.springframework.security.core.userdetails.User;
And have role_ ACTIVITI_ The role of user.
2 configuration class
Like the demo, just inject UserDetailsService.
@Bean public UserDetailsService activitiUserDetailsService() { return new ActivitiUserDetailsManager(userService,groupManager); }
This time, the parameter {userService and groupmanager are the user / group management services of your own business system. I passed them in here through the constructor. You can also inject them in other ways. To put it bluntly, the ActivitiUserDetailsManager class simply wraps your own userService, which can be decoupled from the user service of the workflow.
2 user groups
Wait, the above case only illustrates user query, but what about user groups? In fact, activiti 7 has a default implementation
org.activiti.core.common.spring.identity.ActivitiUserGroupManagerImpl
Among them, there are two most important methods: obtaining user groups and obtaining user roles
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()); }
In fact, this is also done through the user query userDetailsService, and the role is queried through prefix matching, GROUP_ It is not difficult to understand the following code in the demo
{"system", "password", "ROLE_ACTIVITI_USER"},
{"admin", "password", "ROLE_ACTIVITI_ADMIN"}
Therefore, copy the implementation of ActivitiUserGroupManagerImpl and create a new implementation of ActivitiGroupManagerImpl
org.activiti.api.runtime.shared.identity.UserGroupManager
Just rewrite the relevant methods. It is worth noting that the @ Primary annotation needs to be added
Because the default ActivitiUserGroupManagerImpl is also automatically initialized by spring, plus @Primary This is to tell spring to use ActivitiGroupManagerImpl when there are multiple implementation classes shi.
At this point, you can fully access your identity system, including the ability of users and user groups. For the versions below activiti7, the implementation will be very different, but the official document gives the solution, as shown below:
https://www.activiti.org/userguide/index.html#advanced.custom.session.manager