1. 概要

このクイック記事では、Springセキュリティでユーザーを認証するために複数のメカニズムを使用することに焦点を当てます。

これを行うには、複数の認証プロバイダーを構成します。

2. 認証プロバイダー

AuthenticationProvider は、特定のリポジトリ(データベース LDAP カスタムサードパーティソースなど)からユーザー情報をフェッチするための抽象化です。 。 ). フェッチされたユーザー情報を使用して、提供された資格情報を検証します。

簡単に言うと、複数の認証プロバイダーが定義されている場合、プロバイダーは宣言された順序で照会されます。

簡単なデモンストレーションとして、カスタム認証プロバイダーとメモリ内認証プロバイダーの2つの認証プロバイダーを構成します。

3. Mavenの依存関係

まず、必要なSpringセキュリティ依存関係をWebアプリケーションに追加しましょう。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

そして、Spring Bootなし:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>5.2.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
    <version>5.2.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>5.2.2.RELEASE</version>
</dependency>

これらの依存関係の最新バージョンは、 spring-security-web spring-security-core 、およびspring-security-configにあります。

4. カスタム認証プロバイダー

次に、を実装してカスタム認証プロバイダーを作成しましょう。 AuthneticationProvider インターフェース

authenticate メソッドを実装します–認証を試みます。 入力Authenticationオブジェクトには、ユーザーが指定したユーザー名とパスワードのクレデンシャルが含まれています。

authenticate メソッドは、認証が成功した場合、完全に入力されたAuthenticationオブジェクトを返します。 認証が失敗すると、タイプAuthenticationExceptionの例外がスローされます。

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
    @Override
    public Authentication authenticate(Authentication auth) 
      throws AuthenticationException {
        String username = auth.getName();
        String password = auth.getCredentials()
            .toString();

        if ("externaluser".equals(username) && "pass".equals(password)) {
            return new UsernamePasswordAuthenticationToken
              (username, password, Collections.emptyList());
        } else {
            throw new 
              BadCredentialsException("External system authentication failed");
        }
    }

    @Override
    public boolean supports(Class<?> auth) {
        return auth.equals(UsernamePasswordAuthenticationToken.class);
    }
}

当然、これはここでの例の目的のための単純な実装です。

5. 複数の認証プロバイダーの構成

次に、CustomAuthenticationProviderとメモリ内認証プロバイダーをSpringセキュリティ構成に追加しましょう。

5.1. Java構成

構成クラスで、AuthenticationManagerBuilderを使用して認証プロバイダーを作成および追加しましょう。

最初にCustomAuthenticationProvider、次に inMemoryAuthentication()を使用したメモリ内認証プロバイダー。

また、URLパターン「 / api /**」へのアクセスを認証する必要があることも確認しています。

@EnableWebSecurity
public class MultipleAuthProvidersSecurityConfig 
  extends WebSecurityConfigurerAdapter {
    @Autowired
    CustomAuthenticationProvider customAuthProvider;

    @Override
    public void configure(AuthenticationManagerBuilder auth) 
      throws Exception {

        auth.authenticationProvider(customAuthProvider);
        auth.inMemoryAuthentication()
            .withUser("memuser")
            .password(encoder().encode("pass"))
            .roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic()
            .and()
            .authorizeRequests()
            .antMatchers("/api/**")
            .authenticated();
    }
    
     
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

5.2. XML構成

または、Java構成の代わりにXML構成を使用する場合は、次のようにします。

<security:authentication-manager>
    <security:authentication-provider>
        <security:user-service>
            <security:user name="memuser" password="pass" 
              authorities="ROLE_USER" />
        </security:user-service>
    </security:authentication-provider>
    <security:authentication-provider
      ref="customAuthenticationProvider" />
</security:authentication-manager>

<security:http>
    <security:http-basic />
    <security:intercept-url pattern="/api/**" 
      access="isAuthenticated()" />
</security:http>

6. アプリケーション

次に、2つの認証プロバイダーによって保護される単純なRESTエンドポイントを作成しましょう。

このエンドポイントにアクセスするには、有効なユーザー名とパスワードを指定する必要があります。 認証プロバイダーは、資格情報を検証し、アクセスを許可するかどうかを決定します。

@RestController
public class MultipleAuthController {
    @GetMapping("/api/ping")
    public String getPing() {
        return "OK";
    }
}

7. テスト

最後に、安全なアプリケーションへのアクセスをテストしましょう。 アクセスは、有効な資格情報が提供されている場合にのみ許可されます。

@Autowired
private TestRestTemplate restTemplate;

@Test
public void givenMemUsers_whenGetPingWithValidUser_thenOk() {
    ResponseEntity<String> result 
      = makeRestCallToGetPing("memuser", "pass");

    assertThat(result.getStatusCodeValue()).isEqualTo(200);
    assertThat(result.getBody()).isEqualTo("OK");
}

@Test
public void givenExternalUsers_whenGetPingWithValidUser_thenOK() {
    ResponseEntity<String> result 
      = makeRestCallToGetPing("externaluser", "pass");

    assertThat(result.getStatusCodeValue()).isEqualTo(200);
    assertThat(result.getBody()).isEqualTo("OK");
}

@Test
public void givenAuthProviders_whenGetPingWithNoCred_then401() {
    ResponseEntity<String> result = makeRestCallToGetPing();

    assertThat(result.getStatusCodeValue()).isEqualTo(401);
}

@Test
public void givenAuthProviders_whenGetPingWithBadCred_then401() {
    ResponseEntity<String> result 
      = makeRestCallToGetPing("user", "bad_password");

    assertThat(result.getStatusCodeValue()).isEqualTo(401);
}

private ResponseEntity<String> 
  makeRestCallToGetPing(String username, String password) {
    return restTemplate.withBasicAuth(username, password)
      .getForEntity("/api/ping", String.class, Collections.emptyMap());
}

private ResponseEntity<String> makeRestCallToGetPing() {
    return restTemplate
      .getForEntity("/api/ping", String.class, Collections.emptyMap());
}

8. 結論

このクイックチュートリアルでは、Springセキュリティで複数の認証プロバイダーを構成する方法を説明しました。 カスタム認証プロバイダーとメモリ内認証プロバイダーを使用して、単純なアプリケーションを保護しました。

また、アプリケーションへのアクセスに、少なくとも1つの認証プロバイダーが検証できる資格情報が必要であることを確認するためのテストも作成しました。

いつものように、実装の完全なソースコードはGitHubにあります。