Activiti с Spring Security

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.