Activiti с Spring Security
1. обзор
Activiti - это система управления бизнес-процессами с открытым исходным кодом. Для ознакомления посмотрите нашGuide to Activiti with Java.
И Activiti, и среда Spring предоставляют собственное управление идентификацией. Однакоin an application that integrates both projects, we may want to combine the two into a single user management process.
Далее мы рассмотрим две возможности для достижения этого: первая - это предоставление пользовательской службы, поддерживаемой Activiti, для Spring Security, а вторая - подключение пользовательского источника Spring Security к управлению идентификацией Activiti.
2. Maven Зависимости
Чтобы настроить Activiti в проекте Spring Boot, ознакомьтесь сour previous article. Помимоactiviti-spring-boot-starter-basic, нам также понадобится зависимостьactiviti-spring-boot-starter-security:
org.activiti
activiti-spring-boot-starter-security
6.0.0
3. Управление идентификацией с помощью Activiti
Для этого сценария стартеры Activiti предоставляют класс автоконфигурации Spring Boot, который защищает все конечные точки REST с помощью аутентификацииHTTP Basic.
Автоконфигурация также создает bean-компонентUserDetailsService классаIdentityServiceUserDetailsService.
Класс реализует интерфейс SpringUserDetailsService и переопределяет методloadUserByUsername(). Этот метод извлекает объект ActivitiUser с даннымid и использует его для создания объекта SpringUserDetails.
Кроме того, объект ActivitiGroup соответствует роли пользователя Spring.
Это означает, что при входе в приложение Spring Security мы будем использовать учетные данные Activiti.
3.1. Настройка пользователей Activiti
Во-первых, давайте создадим пользователя вInitializingBean, определенном в основном классе@SpringBootApplication, используяIdentityService:
@Bean
InitializingBean usersAndGroupsInitializer(IdentityService identityService) {
return new InitializingBean() {
public void afterPropertiesSet() throws Exception {
User user = identityService.newUser("activiti_user");
user.setPassword("pass");
identityService.saveUser(user);
Group group = identityService.newGroup("user");
group.setName("ROLE_USER");
group.setType("USER");
identityService.saveGroup(group);
identityService.createMembership(user.getId(), group.getId());
}
};
}
Вы заметите, чтоsince this will be used by Spring Security, the Group object name has to be of the form “ROLE_X”.
3.2. Конфигурация Spring Security
Если мы хотим использовать другую конфигурацию безопасности вместо базовой аутентификации HTTP, сначала мы должны исключить автоконфигурацию:
@SpringBootApplication(
exclude = org.activiti.spring.boot.SecurityAutoConfiguration.class)
public class ActivitiSpringSecurityApplication {
// ...
}
Затем мы можем предоставить наш собственный класс конфигурации Spring Security, который используетIdentityServiceUserDetailsService для извлечения пользователей из источника данных Activiti:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private IdentityService identityService;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailsService());
}
@Bean
public UserDetailsService userDetailsService() {
return new IdentityServiceUserDetailsService(
this.identityService);
}
// spring security configuration
}
4. Управление идентификацией с использованием Spring Security
Если у нас уже настроено управление пользователями с помощью Spring Security, и мы хотим добавить Activiti в наше приложение, то нам нужно настроить управление идентификацией Activiti.
Для этого мы должны расширить два основных класса:UserEntityManagerImpl иGroupEntityManagerImpl, которые обрабатывают пользователей и группы.
Давайте рассмотрим каждый из них более подробно.
4.1. РасширениеUserEntityManagerImpl
Давайте создадим наш собственный класс, который расширяет классUserEntityManagerImpl:
public class SpringSecurityUserManager extends UserEntityManagerImpl {
private JdbcUserDetailsManager userManager;
public SpringSecurityUserManager(
ProcessEngineConfigurationImpl processEngineConfiguration,
UserDataManager userDataManager,
JdbcUserDetailsManager userManager) {
super(processEngineConfiguration, userDataManager);
this.userManager = userManager;
}
// ...
}
Для этого класса нужен конструктор приведенной выше формы, а также менеджер пользователей Spring Security. В нашем случае мы использовалиUserDetailsManager. на основе базы данных
Основные методы, которые мы хотим переопределить, - это те, которые обрабатывают поиск пользователя:findById(),findUserByQueryCriteria() иfindGroupsByUser().
МетодfindById() используетJdbcUserDetailsManager, чтобы найти объектUserDetails и преобразовать его в объектUser:
@Override
public UserEntity findById(String userId) {
UserDetails userDetails = userManager.loadUserByUsername(userId);
if (userDetails != null) {
UserEntityImpl user = new UserEntityImpl();
user.setId(userId);
return user;
}
return null;
}
Затем методfindGroupsByUser() находит все полномочия Spring Security пользователя и возвращаетList объектовGroup:
public List findGroupsByUser(String userId) {
UserDetails userDetails = userManager.loadUserByUsername(userId);
if (userDetails != null) {
return userDetails.getAuthorities().stream()
.map(a -> {
Group g = new GroupEntityImpl();
g.setId(a.getAuthority());
return g;
})
.collect(Collectors.toList());
}
return null;
}
МетодfindUserByQueryCriteria() основан на объектеUserQueryImpl с несколькими свойствами, из которых мы извлечем идентификатор группы и идентификатор пользователя, поскольку у них есть корреспонденты в Spring Security:
@Override
public List findUserByQueryCriteria(
UserQueryImpl query, Page page) {
// ...
}
Этот метод следует принципу, аналогичному приведенному выше, путем создания объектовUser из объектовUserDetails. Смотрите ссылку на GitHub в конце для полной реализации.
Точно так же у нас есть методfindUserCountByQueryCriteria():
public long findUserCountByQueryCriteria(
UserQueryImpl query) {
return findUserByQueryCriteria(query, null).size();
}
МетодcheckPassword() всегда должен возвращать истину, поскольку проверка пароля не выполняется Activiti:
@Override
public Boolean checkPassword(String userId, String password) {
return true;
}
Для других методов, например, для обновления пользователей, мы просто выдадим исключение, поскольку это обрабатывается Spring Security:
public User createNewUser(String userId) {
throw new UnsupportedOperationException("This operation is not supported!");
}
4.2. УвеличьтеGroupEntityManagerImpl
SpringSecurityGroupManager похож на класс диспетчера пользователей, за исключением того, что он имеет дело с группами пользователей:
public class SpringSecurityGroupManager extends GroupEntityManagerImpl {
private JdbcUserDetailsManager userManager;
public SpringSecurityGroupManager(ProcessEngineConfigurationImpl
processEngineConfiguration, GroupDataManager groupDataManager) {
super(processEngineConfiguration, groupDataManager);
}
// ...
}
Здесьthe main method to override is the findGroupsByUser() method:
@Override
public List findGroupsByUser(String userId) {
UserDetails userDetails = userManager.loadUserByUsername(userId);
if (userDetails != null) {
return userDetails.getAuthorities().stream()
.map(a -> {
Group g = new GroupEntityImpl();
g.setId(a.getAuthority());
return g;
})
.collect(Collectors.toList());
}
return null;
}
Метод извлекает полномочия пользователя Spring Security и преобразует их в список объектовGroup.
Исходя из этого, мы также можем переопределить методыfindGroupByQueryCriteria() иfindGroupByQueryCriteriaCount():
@Override
public List findGroupByQueryCriteria(GroupQueryImpl query, Page page) {
if (query.getUserId() != null) {
return findGroupsByUser(query.getUserId());
}
return null;
}
@Override
public long findGroupCountByQueryCriteria(GroupQueryImpl query) {
return findGroupByQueryCriteria(query, null).size();
}
Другие методы, которые обновляют группы, могут быть переопределены, чтобы вызвать исключение:
public Group createNewGroup(String groupId) {
throw new UnsupportedOperationException("This operation is not supported!");
}
4.3. Конфигурация движка процесса
После определения двух классов диспетчера идентификаторов нам нужно подключить их к конфигурации.
Пружинные стартеры автоматически настраиваютSpringProcessEngineConfiguration для нас. Чтобы изменить это, мы можем использоватьInitializingBean:
@Autowired
private SpringProcessEngineConfiguration processEngineConfiguration;
@Autowired
private JdbcUserDetailsManager userManager;
@Bean
InitializingBean processEngineInitializer() {
return new InitializingBean() {
public void afterPropertiesSet() throws Exception {
processEngineConfiguration.setUserEntityManager(
new SpringSecurityUserManager(processEngineConfiguration,
new MybatisUserDataManager(processEngineConfiguration), userManager));
processEngineConfiguration.setGroupEntityManager(
new SpringSecurityGroupManager(processEngineConfiguration,
new MybatisGroupDataManager(processEngineConfiguration)));
}
};
}
Здесь существующийprocessEngineConfiguration изменен для использования наших настраиваемых менеджеров удостоверений.
Если мы хотим установить текущего пользователя в Activiti, мы можем использовать метод:
identityService.setAuthenticatedUserId(userId);
Имейте в виду, что это устанавливает свойствоThreadLocal, поэтому значение различается для каждого потока.
5. Заключение
В этой статье мы рассмотрели два способа интеграции Activiti с Spring Security.
Полный исходный код можно найтиover on GitHub.