1. 序章

このチュートリアルでは、構成内の2つの異なるSpring Security http 要素を使用して、2つの異なるログインページで動作するようにSpringSecurityを構成する方法を説明します。

2. 2つのHttp要素の構成

2つのログインページが必要になる状況の1つは、アプリケーションの管理者用に1つのページがあり、通常のユーザー用に別のページがある場合です。

それぞれに関連付けられたURLパターンによって区別される2つのhttp要素構成します。

  • アクセスするために通常のユーザー認証が必要なページの場合は/user *
  • 管理者がアクセスするページの場合は/admin *

http要素には、異なるログインページと異なるログイン処理URLがあります。

2つの異なるhttp要素を構成するために、WebSecurityConfigurerAdapterを拡張する@Configurationアノテーションが付けられた2つの静的クラスを作成しましょう。

どちらも通常の@Configurationクラス内に配置されます。

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    ...
}

「ADMIN」ユーザーのWebSecurityConfigurerAdapterを定義しましょう。

@Configuration
@Order(1)
public static class App1ConfigurationAdapter extends WebSecurityConfigurerAdapter {
    public App1ConfigurationAdapter() {
        super();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/admin*")
          .authorizeRequests()
          .anyRequest()
          .hasRole("ADMIN")
          
          .and()
          .formLogin()
          .loginPage("/loginAdmin")
          .loginProcessingUrl("/admin_login")
          .failureUrl("/loginAdmin?error=loginError")
          .defaultSuccessUrl("/adminPage")
          
          .and()
          .logout()
          .logoutUrl("/admin_logout")
          .logoutSuccessUrl("/protectedLinks")
          .deleteCookies("JSESSIONID")
          
          .and()
          .exceptionHandling()
          .accessDeniedPage("/403")
          
          .and()
          .csrf().disable();
    }
}

それでは、通常のユーザー向けにWebSecurityConfigurerAdapterを定義しましょう。

@Configuration
@Order(2)
public static class App2ConfigurationAdapter extends WebSecurityConfigurerAdapter {

    public App2ConfigurationAdapter() {
        super();
    }

    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/user*")
          .authorizeRequests()
          .anyRequest()
          .hasRole("USER")
          
          .and()
          .formLogin()
          .loginPage("/loginUser")
          .loginProcessingUrl("/user_login")
          .failureUrl("/loginUser?error=loginError")
          .defaultSuccessUrl("/userPage")
          
          .and()
          .logout()
          .logoutUrl("/user_logout")
          .logoutSuccessUrl("/protectedLinks")
          .deleteCookies("JSESSIONID")
          
          .and()
          .exceptionHandling()
          .accessDeniedPage("/403")
          
          .and()
          .csrf().disable();
    }
}

各静的クラスに@Orderアノテーションを配置することにより、URLが要求されたときにパターンマッチングに基づいて2つのクラスが考慮される順序を指定していることに注意してください。

2つの構成クラスの順序を同じにすることはできません。

3. カスタムログインページ

ユーザーのタイプごとに独自のカスタムログインページを作成します。 管理者ユーザーの場合、ログインフォームには、構成で定義されている「user_login」アクションがあります。

<p>User login page</p>
<form name="f" action="user_login" method="POST">
    <table>
        <tr>
            <td>User:</td>
            <td><input type="text" name="username" value=""></td>
        </tr>
        <tr>
            <td>Password:</td>
            <td><input type="password" name="password" /></td>
        </tr>
        <tr>
            <td><input name="submit" type="submit" value="submit" /></td>
        </tr>
    </table>
</form>

管理者のログインページも同様ですが、フォームにはJava構成に従って「admin_login」のアクションがあります。

4. 認証構成

次に、アプリケーションの認証を構成する必要があります。 これを実現する2つの方法を見てみましょう。1つはユーザー認証に共通のソースを使用し、もう1つは2つの別々のソースを使用します。

4.1. 共通ユーザー認証ソースの使用

両方のログインページがユーザーを認証するための共通のソースを共有している場合、認証を処理するタイプUserDetailsServiceの単一のBeanを作成できます。

InMemoryUserDetailsManager を使用してこのシナリオを示しましょう。1つは「USER」の役割を持ち、もう1つは「ADMIN」の役割を持ちます。

@Bean
public UserDetailsService userDetailsService() throws Exception {
    InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
    manager.createUser(User
      .withUsername("user")
      .password(encoder().encode("userPass"))
      .roles("USER")
      .build());
    
    manager.createUser(User
      .withUsername("admin")
      .password(encoder().encode("adminPass"))
      .roles("ADMIN")
      .build());
    
    return manager;
}

@Bean
public static PasswordEncoder encoder() {
    return new BCryptPasswordEncoder();
}

4.2. 2つの異なるユーザー認証ソースの使用

ユーザー認証のソースが異なる場合(管理者用と通常ユーザー用)、各静的@Configurationクラス内でAuthenticationManagerBuilderを構成できます。 「ADMIN」ユーザーの認証マネージャーの例を見てみましょう。

@Configuration
@Order(1)
public static class App1ConfigurationAdapter extends WebSecurityConfigurerAdapter { 
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
          .withUser("admin")
          .password(encoder().encode("admin"))
          .roles("ADMIN");
    }
}

この場合、前のセクションのUserDetailsServiceBeanは使用されなくなります。

6. 結論

このクイックチュートリアルでは、同じSpringSecurityアプリケーションに2つの異なるログインページを実装する方法を示しました。

この記事の完全なコードは、GitHubプロジェクトにあります。

アプリケーションを実行すると、 / protectedLinksURIで上記の例にアクセスできます。