Activiti mit Spring Security

Activiti mit Frühlingssicherheit

1. Überblick

Activiti ist ein Open-Source-BPM-System (Business Process Management). Eine Einführung finden Sie in unserenGuide to Activiti with Java.

Sowohl Activiti als auch das Spring-Framework bieten ein eigenes Identitätsmanagement. in an application that integrates both projects, we may want to combine the two into a single user management process.

Im Folgenden werden zwei Möglichkeiten untersucht, um dies zu erreichen: Zum einen durch Bereitstellung eines von Activiti unterstützten Benutzerdienstes für Spring Security und zum anderen durch Einfügen einer Spring Security-Benutzerquelle in das Activiti-Identitätsmanagement.

2. Maven-Abhängigkeiten

Überprüfen Sieour previous article, um Activiti in einem Spring Boot-Projekt einzurichten. Zusätzlich zuactiviti-spring-boot-starter-basic, benötigen wir auch die Abhängigkeit vonactiviti-spring-boot-starter-security:


    org.activiti
    activiti-spring-boot-starter-security
    6.0.0

3. Identitätsmanagement mit Activiti

In diesem Szenario stellen die Activiti-Starter eine automatische Spring Boot-Konfigurationsklasse bereit, die alle REST-Endpunkte mit der Authentifizierung vonHTTP Basicichert.

Die automatische Konfiguration erstellt auch eineUserDetailsService-Bean der KlasseIdentityServiceUserDetailsService.

Die Klasse implementiert die Spring-SchnittstelleUserDetailsService und überschreibt dieloadUserByUsername()-Methode. Diese Methode ruft das Objekt eines ActivitiUsermit dem angegebenenid ab und erstellt daraus ein SpringUserDetails-Objekt.

Außerdem entspricht das Objekt ActivitiGroupeiner Spring-Benutzerrolle.

Dies bedeutet, dass wir bei der Anmeldung bei der Spring Security-Anwendung Activiti-Anmeldeinformationen verwenden.

3.1. Aktivieren von Activiti-Benutzern

Erstellen wir zunächst einen Benutzer inInitializingBean, der in der Hauptklasse@SpringBootApplication definiert ist, und verwenden Sie dabeiIdentityService:

@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());
        }
    };
}

Sie werden feststellen, dasssince this will be used by Spring Security, the Group object name has to be of the form “ROLE_X”.

3.2. Spring-Sicherheitskonfiguration

Wenn wir anstelle der HTTP-Standardauthentifizierung eine andere Sicherheitskonfiguration verwenden möchten, müssen wir zuerst die automatische Konfiguration ausschließen:

@SpringBootApplication(
  exclude = org.activiti.spring.boot.SecurityAutoConfiguration.class)
public class ActivitiSpringSecurityApplication {
    // ...
}

Anschließend können wir unsere eigene Spring Security-Konfigurationsklasse bereitstellen, dieIdentityServiceUserDetailsServiceverwendet, um Benutzer aus der Activiti-Datenquelle abzurufen:

@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. Identitätsverwaltung mit Spring Security

Wenn wir die Benutzerverwaltung bereits mit Spring Security eingerichtet haben und Activiti zu unserer Anwendung hinzufügen möchten, müssen wir die Identitätsverwaltung von Activiti anpassen.

Zu diesem Zweck müssen wir zwei Hauptklassen erweitern:UserEntityManagerImpl undGroupEntityManagerImpl, die Benutzer und Gruppen behandeln.

Schauen wir uns diese genauer an.

4.1. UserEntityManagerImpl verlängern

Erstellen wir unsere eigene Klasse, die die KlasseUserEntityManagerImplerweitert:

public class SpringSecurityUserManager extends UserEntityManagerImpl {

    private JdbcUserDetailsManager userManager;

    public SpringSecurityUserManager(
      ProcessEngineConfigurationImpl processEngineConfiguration,
      UserDataManager userDataManager,
      JdbcUserDetailsManager userManager) {

        super(processEngineConfiguration, userDataManager);
        this.userManager = userManager;
    }

    // ...
}

Diese Klasse benötigt einen Konstruktor des obigen Formulars sowie den Spring Security-Benutzermanager. In unserem Fall haben wir ein datenbankgestütztesUserDetailsManager. verwendet

Die wichtigsten Methoden, die wir überschreiben möchten, sind diejenigen, die das Abrufen von Benutzern behandeln:findById(),findUserByQueryCriteria() undfindGroupsByUser().

Die MethodefindById() verwendetJdbcUserDetailsManager, um einUserDetails-Objekt zu finden und es in einUser-Objekt umzuwandeln:

@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;
}

Als Nächstes findet die MethodefindGroupsByUser() alle Spring Security-Berechtigungen eines Benutzers und gibtList vonGroup Objekten zurück:

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;
}

Die MethodefindUserByQueryCriteria()basiert auf einemUserQueryImpl-Objekt mit mehreren Eigenschaften, aus denen wir die Gruppen-ID und die Benutzer-ID extrahieren, da sie in Spring Security Korrespondenten haben:

@Override
public List findUserByQueryCriteria(
  UserQueryImpl query, Page page) {
    // ...
}

Diese Methode folgt einem ähnlichen Prinzip wie oben, indemUser Objekte ausUserDetails Objekten erstellt werden. Die vollständige Implementierung finden Sie unter dem GitHub-Link am Ende.

Ebenso haben wir diefindUserCountByQueryCriteria()-Methode:

public long findUserCountByQueryCriteria(
  UserQueryImpl query) {

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

Die MethodecheckPassword()ollte immer true zurückgeben, da die Kennwortüberprüfung nicht von Activiti durchgeführt wird:

@Override
public Boolean checkPassword(String userId, String password) {
    return true;
}

Bei anderen Methoden, z. B. beim Aktualisieren von Benutzern, wird nur eine Ausnahme ausgelöst, da dies von Spring Security behandelt wird:

public User createNewUser(String userId) {
    throw new UnsupportedOperationException("This operation is not supported!");
}

4.2. Erweitern Sie dieGroupEntityManagerImpl

SpringSecurityGroupManager ähnelt der Benutzermanagerklasse, mit der Ausnahme, dass es sich um Benutzergruppen handelt:

public class SpringSecurityGroupManager extends GroupEntityManagerImpl {

    private JdbcUserDetailsManager userManager;

    public SpringSecurityGroupManager(ProcessEngineConfigurationImpl
      processEngineConfiguration, GroupDataManager groupDataManager) {
        super(processEngineConfiguration, groupDataManager);
    }

    // ...
}

Hierthe 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;
}

Die Methode ruft die Berechtigungen eines Spring Security-Benutzers ab und wandelt sie in eine Liste derGroup-Objekte um.

Auf dieser Grundlage können wir auch die MethodenfindGroupByQueryCriteria() undfindGroupByQueryCriteriaCount() überschreiben:

@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();
}

Andere Methoden zum Aktualisieren von Gruppen können überschrieben werden, um eine Ausnahme auszulösen:

public Group createNewGroup(String groupId) {
    throw new UnsupportedOperationException("This operation is not supported!");
}

4.3. Process Engine-Konfiguration

Nachdem Sie die beiden Identity Manager-Klassen definiert haben, müssen Sie sie in die Konfiguration einbinden.

Die Federstarter konfigurieren automatisch einSpringProcessEngineConfiguration für uns. Um dies zu ändern, können wirInitializingBean: verwenden

@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)));
            }
        };
    }

Hier werden die vorhandenenprocessEngineConfiguration geändert, um unsere benutzerdefinierten Identitätsmanager zu verwenden.

Wenn wir den aktuellen Benutzer in Activiti festlegen möchten, können wir die Methode verwenden:

identityService.setAuthenticatedUserId(userId);

Beachten Sie, dass hierdurch die EigenschaftThreadLocalfestgelegt wird, sodass der Wert für jeden Thread unterschiedlich ist.

5. Fazit

In diesem Artikel haben wir die beiden Möglichkeiten gesehen, wie wir Activiti in Spring Security integrieren können.

Der vollständige Quellcode istover on GitHub.