Spring Securityでフォーム認証を実装する(設定編)ー Spring Security ver 5.7.0より前の場合

SpringSecirityはバージョンごとに設定の書き方が大幅に変更されている。
ここでは、バージョンごとに記載方法が大きく変更となっているJavaConfig の記載方法についてまとめる。

バージョンによらない部分の実装については、下記の記事参照。

olafnosuke.hatenablog.com


@EnableWebSecurityアノテーションを付ける

このクラスに記載の設定を適用したSpringSecurityによるフォーム認証を有効化するために、「@EnableWebSecurityアノテーションをクラスに付与する。

@EnableWebSecurityは合成アノテーションであり、内部に「@Configurationアノテーションも持っているため、
このクラスはJavaConfigクラスではあるものの「@Configurationアノテーションの付与を省略することができる。

@EnableWebSecurity
public class FormLoginConfiguration {
}

「WebSecurityConfigurerAdapter」を実装する

org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapterをextendsする。

@EnableWebSecurity
public class FormLoginConfiguration extends WebSecurityConfigurerAdapter {
}

パスワードのエンコーダクラスをDIコンテナに登録する

作成したJavaConfigクラス内でパスワードのエンコーダクラスをDIコンテナに登録するメソッドを実装する。
使用するエンコーダはプロジェクトの仕様により適切なものを選定する。

/**
 * パスワードのエンコーダーをDIコンテナに登録する。<br>
 */
@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

ユーザ情報を構築するサービスをDIコンテナに登録する

作成したJavaConfigクラス内でユーザ情報を構築するサービスをDIコンテナに登録するメソッドを実装する。
WebSecurityConfigurerAdapterクラスにUserDetailsServiceを設定するためのメソッドが存在しているため、
そのメソッドをオーバーライドする。

/**
 * ユーザー情報を構築するサービスをDIコンテナに登録する。<br>
 */
@Bean
@Override
public UserDetailsService userDetailsService() {
    return new LocalUserDetailService();
}

引数が「AuthenticationManagerBuilder」なconfigureメソッドをオーバーライドする

オーバーライドしたメソッドの中で、DIコンテナに登録したパスワードエンコーダーと、ユーザ情報を構築するサービスを設定する処理を追加する。

/**
 * {@inheritDoc}
 */
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
  auth
      .userDetailsService(userDetailsService())
      .passwordEncoder(passwordEncoder());
}

引数が「HttpSecurity」なconfigureメソッドをオーバーライドする

このメソッドの中では、以下の設定を実施する。 + 認証の対象とするURLの設定 + フォーム認証の設定 + ログアウトの設定

/**
 * {@inheritDoc}
 */
@Override
protected void configure(HttpSecurity http) throws Exception {
}
認証の対象とするURLの設定

フォーム認証の対象とするURL、対象外とするURLを設定する。
静的ファイルのパスやログイン画面のURLを認証対象外となるように設定している。

/**
 * {@inheritDoc}
 */
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        // 認証対象外のURL指定
        .mvcMatchers("/images/**", "/css/**", "/js/**", "/webjars/**", "/authorization/**").permitAll()
        // ログイン画面のURLも認証対象外とする
        .antMatchers("/login").permitAll()
        // 上記のURL以外は認証が必要
        .anyRequest().authenticated();
}
フォーム認証の設定

フォーム認証で使用するログイン画面へ遷移するURL、フォーム認証成功後に遷移するURLを指定する。
また、認証成功時にユーザオブジェクトを構築するハンドラーを追加する。

/**
 * {@inheritDoc}
 */
@Override
protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                // フォーム認証のログイン画面のURL
                .loginPage("/login")
                // 認証成功時に遷移するURL
                .defaultSuccessUrl("/")
                // Form認証成功時にユーザオブジェクトを構築するハンドラーを追加する
                .successHandler(new LocalAuthenticationSuccessHandler());
}
ログアウトの設定

ログアウトURLやログアウト成功時の遷移先URLの設定を行う。
また、ログアウト時にCookieの値やセッションを無効化するよう設定を行う。

/**
 * {@inheritDoc}
 */
@Override
protected void configure(HttpSecurity http) throws Exception {
        http.logout()
                // ログアウトのURL
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                // ログアウト成功時のURL(ログイン画面に遷移)
                .logoutSuccessUrl("/login")
                // Cookieの値を削除する
                .deleteCookies("JSESSIONID")
                // セッションを無効化する
                .invalidateHttpSession(true).permitAll();
}

最終的に作成されるJavaConfigクラス

最終的に作成されるクラスの全体像は以下の通り。

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import jp.co.sample.web.authentication.handler.LocalAuthenticationSuccessHandler;
import jp.co.sample.web.authentication.user.LocalUserDetailService;

/**
 * フォーム認証する際に使用するSpring Securityの定義クラス。<br>
 */
@EnableWebSecurity
public class FormLoginConfiguration extends WebSecurityConfigurerAdapter {
    /**
     * パスワードのエンコーダーをDIコンテナに登録する。<br>
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * ユーザー情報を構築するサービスをDIコンテナに登録する。<br>
     */
    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        return new LocalUserDetailService();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 認証処理の設定
        http.authorizeRequests()
            // 認証対象外のURL指定
            .mvcMatchers("/images/**", "/css/**", "/js/**", "/webjars/**", "/authorization/**").permitAll()
            // ログイン画面のURLも認証対象外とする
            .antMatchers("/login").permitAll()
            // 上記のURL以外は認証が必要
            .anyRequest().authenticated();

        // Form認証の設定
        http.formLogin()
                // フォーム認証のログイン画面のURL
                .loginPage("/login")
                // 認証成功時に遷移するURL
                .defaultSuccessUrl("/")
                // Form認証成功時にユーザオブジェクトを構築するハンドラーを追加する
                .successHandler(new LocalAuthenticationSuccessHandler());

        // ログアウト処理の設定
        http.logout()
                // ログアウトのURL
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                // ログアウト成功時のURL(ログイン画面に遷移)
                .logoutSuccessUrl("/login")
                // Cookieの値を削除する
                .deleteCookies("JSESSIONID")
                // セッションを無効化する
                .invalidateHttpSession(true).permitAll();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userDetailsService())
                .passwordEncoder(passwordEncoder());
    }
}