SpringSecurityでユーザー認証機能を簡単につくる

SpringBootでWebアプリケーションを作る際、
SpringSecurityを使ってあれこれカスタマイズしながらユーザー認証の仕組みを作ったのでまとめます

# SpringConfigクラスを作る

```spring:SpringConfig.javaの例

@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    // ポイント1
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/css/**", "/image/**", "/js/**");
    }

    // ポイント2
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/signin").permitAll()
                                .antMatchers("/admin/**").hasRole("ADMIN")
                                .anyRequest().authenticated();
        http.formLogin().loginProcessingUrl("/login").loginPage("/signin")
                        .failureUrl("?error").defaultSuccessUrl("/", false)
                        .usernameParameter("loginId").passwordParameter("password")
                        .and().logout().logoutRequestMatcher(new AntPathRequestMatcher("signout"))
                        .logoutSuccessUrl("/signin")
                        .deleteCookies("JSESSIONID")
                        .invalidateHttpSession(true).permitAll();
        http.sessionManagement().invalidSessionUrl("/signin");
    }

    // ポイント3
    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication()
                .dataSource(dataSource)
                .usersByUsernameQuery(
                        "select mail_address as username, password, enabled from accounts where mail_address = ?")
                .authoritiesByUsernameQuery(
                        "select mail_address, role from accounts where mail_address = ?")
                .passwordEncoder(new ShaPasswordEncoder(256));
    }
}
```

# ポイント1 の部分
* ここではSpringSecurityの制限を無視してほしい場所の指定をします
    * 例： 静的ファイルなどを置いている場所
* 例えばロゴなどをサイトのヘッダーに置いている時など、これがないとロゴが出なくなってしまいます！


# ポイント2の部分
* ここでは認証のための設定をしています
* まず上から順番に以下の部分です

```
http.authorizeRequests().antMatchers("/signin").permitAll()
                        .antMatchers("/admin/**").hasRole("ADMIN")
                        .anyRequest().authenticated(); 
```
* ここではユーザーの持っている権限によってみれるページを制限しています
    * antMatchers("/signin") ← ログイン画面
        * ここには全てのユーザーがアクセス可

    * antMatchers("/admin/**") ← 管理画面
        * ここには"ADMIN"権限がないとアクセスできない
        * Userテーブルのroleのカラムに入っている"ROLE_ADMIN＂の"ROLE_"部分を取り除いたもので認証されます

    * .anyRequest().authenticated()
        * 全てのURLリクエストは認証されているユーザーしかアクセスできないという記述です


* 次に以下の部分です

```
http.formLogin().loginProcessingUrl("/login").loginPage("/signin")
                .failureUrl("?error").defaultSuccessUrl("/", false)
                .usernameParameter("loginId").passwordParameter("password")
                .and().logout().logoutRequestMatcher(new AntPathRequestMatcher("signout"))
                .logoutSuccessUrl("/signin")
                .deleteCookies("JSESSIONID")
                .invalidateHttpSession(true).permitAll();
```

* ここでは認証に関わるパラメータを指定してます
    * .loginProcessingUrl("/login")
        * ログインの処理をするURL
    * .loginPage("/signin")
        * ログイン画面のURL
    * .failureUrl("?error")
        * ログインに失敗した時のURL
        * ?errorとつけておくとthymeleafの方でエラーのメッセージを出すときに便利です
    * .defaultSuccessUrl("/", false)
        * ログインが成功した時のURL(ここではindex.htmlなので"/"のみで指定可)
        * 第2引数のboolean
            * true : ログイン画面した後必ずtopにとばされる
            * false : (認証されてなくて一度ログイン画面に飛ばされても)ログインしたら指定したURLに飛んでくれる
    * .usernameParameter("loginId").passwordParameter("password")
        * ログイン画面のhtmlのinputのname属性を見に行っている
    * .logoutRequestMatcher(new AntPathRequestMatcher("signout"))
        * ログアウトのURL
    * .logoutSuccessUrl("/signin")
        * ログアウト成功したあとのURL
    * .deleteCookies("JSESSIONID")
        * ログアウトしたら cookieの　JSESSIONID を削除
    * .invalidateHttpSession(true)
        * ログアウトしたらセッションを無効にする

# ポイント3の部分
* ここでは認証のためのクエリを書いています
* ログインをするだけであればここに書いておけば他にクラスを作る必要がありません！

```
    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication()
                .dataSource(dataSource)
                .usersByUsernameQuery(
                        "select mail_address as username, password, enabled from accounts where mail_address = ?")
                .authoritiesByUsernameQuery(
                        "select mail_address, role from accounts where mail_address = ?")
                .passwordEncoder(new ShaPasswordEncoder(256));
    }

```

* "select mail_address as username, password, enabled from accounts where mail_address = ?"
    * このクエリによって、入力されたユーザー名、パスワードがDBの値と一致しているかを見ている
        * (この場合はメールアドレスをユーザー名としています)
        * メールアドレスを認証のIDとしたことで困ったことがあったので注意です(後日追記します)
    * Userテーブルにはenabledというカラムが必須になっています！
        * ここでbool値で有効なアカウントか無効なアカウントかをみれるようになっています

* "select mail_address, role from accounts where mail_address = ?"
    * ユーザーの持つ権限の取得


# まとめ
* SpringSecurityはここのクラスの設定1つでログイン機能が簡単に作れてしまうので便利でした！
* ただ、細かい設定の仕方、パラメーターの指定の仕方を一つ一つ探すのがとても大変だったので、まとめました
* 次はこれを実装していて困ったことがいくつかあったのでその内容をまとめたいと思います