Перейти к основному содержимому

Действия с Spring Security

· 6 мин. чтения

1. Обзор

Activiti — это система BPM (управление бизнес-процессами) с открытым исходным кодом. Для ознакомления ознакомьтесь с нашим руководством по Activiti с Java .

И Activiti, и среда Spring обеспечивают собственное управление идентификацией. Однако в приложении, объединяющем оба проекта, мы можем захотеть объединить их в единый процесс управления пользователями.

Далее мы рассмотрим две возможности для достижения этого: один — предоставить поддерживаемую Activiti службу пользователя для Spring Security, а другой — подключить источник пользователя Spring Security к управлению идентификацией Activiti.

2. Зависимости Maven

Чтобы настроить Activiti в проекте Spring Boot, ознакомьтесь с нашей предыдущей статьей . В дополнение к activiti-spring-boot-starter-basic нам также понадобится зависимость activiti-spring-boot-starter-security :

<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-security</artifactId>
<version>6.0.0</version>
</dependency>

3. Управление идентификацией с помощью Activiti

Для этого сценария стартеры Activiti предоставляют класс автоматической настройки Spring Boot, который защищает все конечные точки REST с помощью базовой проверки подлинности HTTP.

Автоматическая настройка также создает bean-компонент UserDetailsService класса IdentityServiceUserDetailsService.

Класс реализует интерфейс Spring UserDetailsService и переопределяет метод loadUserByUsername() . Этот метод извлекает объект пользователя Activiti с заданным идентификатором и использует его для создания объекта Spring UserDetails .

Кроме того, объект Activiti Group соответствует роли пользователя 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());
}
};
}

Вы заметите, что, поскольку это будет использоваться Spring Security, имя объекта Group должно иметь форму «ROLE_X» .

3.2. Конфигурация безопасности Spring

Если мы хотим использовать другую конфигурацию безопасности вместо базовой 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 пользователя и возвращает список объектов Group :

public List<Group> 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<User> findUserByQueryCriteria(
UserQueryImpl query, Page page) {
// ...
}

Этот метод следует тому же принципу, что и описанные выше, путем создания объектов User из объектов UserDetails . См. ссылку GitHub в конце для полной реализации.

Точно так же у нас есть метод findUserCountByQueryCriteria() :

public long findUserCountByQueryCriteria(
UserQueryImpl query) {

return findUserByQueryCriteria(query, null).size();
}

Метод checkPassword() всегда должен возвращать значение true, так как проверка пароля не выполняется 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);
}

// ...
}

Здесь основным методом переопределения является метод findGroupsByUser() :

@Override
public List<Group> 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<Group> 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.

Полный исходный код можно найти на GitHub .