1. 概要

Activitiは、オープンソースのBPM(ビジネスプロセス管理)システムです。 はじめに、Javaを使用したActivitiのガイドを確認してください。

ActivitiとSpringフレームワークはどちらも、独自のID管理を提供します。 ただし、両方のプロジェクトを統合するアプリケーションでは、2つを1つのユーザー管理プロセスに結合したい場合があります。

以下では、これを実現するための2つの可能性について説明します。1つはSpring SecurityにActivitiがサポートするユーザーサービスを提供する方法で、もう1つはSpringSecurityユーザーソースをActivitiID管理に接続する方法です。

2. Mavenの依存関係

Spring BootプロジェクトでActivitiを設定するには、以前の記事を確認してください。 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を使用したID管理

このシナリオでは、Activitiスターターは、 HTTPBasic認証ですべてのRESTエンドポイントを保護するSpringBoot自動構成クラスを提供します。

自動構成により、クラスIdentityServiceUserDetailsServiceのUserDetailsServiceBeanも作成されます。

このクラスは、Springインターフェイス UserDetailsService を実装し、 loadUserByUsername()メソッドをオーバーライドします。 このメソッドは、指定されたidを持つActivitiUser オブジェクトを取得し、それを使用してSpring UserDetailsオブジェクトを作成します。

また、Activiti GroupオブジェクトはSpringユーザーロールに対応しています。

これは、Spring Securityアプリケーションにログインするときに、Activitiクレデンシャルを使用することを意味します。

3.1. Activitiユーザーの設定

まず、 IdentityService:を使用して、メインの@SpringBootApplicationクラスで定義されたInitializingBeanにユーザーを作成しましょう。

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

これはSpringSecurityによって使用されるため、グループオブジェクト名は「ROLE_X」の形式である必要があります。

3.2. Springセキュリティ構成

HTTP基本認証の代わりに別のセキュリティ構成を使用する場合は、最初に自動構成を除外する必要があります。

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

次に、IdentityServiceUserDetailsServiceを使用してActivitiデータソースからユーザーを取得する独自のSpringSecurity構成クラスを提供できます。

@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. SpringSecurityを使用したID管理

Spring Securityを使用してユーザー管理を既に設定していて、アプリケーションにActivitiを追加する場合は、ActivitiのID管理をカスタマイズする必要があります。

この目的のために、拡張する必要のある2つの主要なクラスがあります。ユーザーとグループを処理するUserEntityManagerImplGroupEntityManagerImplです。

これらのそれぞれをより詳細に見てみましょう。

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

このクラスには、上記の形式のコンストラクターと、SpringSecurityユーザーマネージャーが必要です。 この例では、データベースに裏打ちされた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オブジェクトのListを返します。

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に対応するものがあるため、グループIDとユーザーIDを抽出します。

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

このメソッドは、 UserDetailsオブジェクトからUserオブジェクトを作成することにより、上記のメソッドと同様の原則に従います。 完全な実装については、最後にあるGitHubリンクを参照してください。

同様に、 findUserCountByQueryCriteria()メソッドがあります。

public long findUserCountByQueryCriteria(
  UserQueryImpl query) {
 
    return findUserByQueryCriteria(query, null).size();
}

checkPassword()メソッドは、パスワードの検証がActivitiによって行われないため、常にtrueを返す必要があります。

@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. プロセスエンジンの構成

2つのIDマネージャークラスを定義した後、それらを構成に接続する必要があります。

スプリングスターターは、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 が、カスタムIDマネージャーを使用するように変更されています。

Activitiで現在のユーザーを設定する場合は、次の方法を使用できます。

identityService.setAuthenticatedUserId(userId);

これによりThreadLocalプロパティが設定されるため、値はスレッドごとに異なることに注意してください。

5. 結論

この記事では、ActivitiをSpringSecurityと統合する2つの方法を見てきました。

完全なソースコードは、GitHubにあります。