Spring Security にてフォーム認証のテストを実施した際に 404 エラーが発生する

# 概要

Spring Security にて下記の構成のフォーム認証のテストを実施した際に、404 エラーが発生しました。

<pre>
                       POST 
            --------------------------------->
フォーム画面                                      AuthenticationProvider (認証処理)              
・ユーザー名   
・パスワード    <=================================
                      ユーザー情報
</pre>

```SecurityConfig.java
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers("/webjars/**", "/css/");
    }

    @Override
    protected void configure(HttpSecurity http) {
        http.authorizeRequests()
                .antMatchers("signin").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/authentication")
                .loginPage("signin")
                .failureUrl("signin" + "?error")
                .successForwardUrl("/hoge/list")
                .failureForwardUrl("/authenticationError")
                .usernameParameter("username")
                .passwordParameter("password")
                .and()
                .logout()
                .logoutSuccessUrl("signin");

}
```

```TestClass.java
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(classes = HogeApplication.class)
public class TestClass {

    @Before
    public void リクエストの用意する() {
        mvc = MockMvcBuilders
                .webAppContextSetup(context)
                .build();
    }

    @Test
    public void リクエストURLで認証する() throws Exception {
        ResultActions result = mvc.perform(
                MockMvcRequestBuilders.post("/authentication")
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .param("username", ユーザーアカウント)
                .param("password", パスワード)
        );
        result.andExpect(status().isOk())
                .andExpect(forwardedUrl("/hoge/list"));
    }
}
```

<pre>
Assertion の結果

java.lang.AssertionError: Status 
Expected :200
Actual   :404

</pre>

# 回避策
下記 2 つの対処策を実施します。

+ 要求時に csrf() を指定する
+ MockMvcBuider に springSecurity() を適用する

上記を適用するとテストクラスは下記のようになります。

```TestClass.java
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(classes = HogeApplication.class)
public class TestClass {

    @Before
    public void リクエストの用意する() {
        mvc = MockMvcBuilders
                .webAppContextSetup(context)
                .apply(springSecurity()) // これを apply しないと csrf を設定しても 404 になってしまう。
                .build();
    }

    @Test
    public void リクエストURLで認証する() throws Exception {
        ResultActions result = mvc.perform(
                MockMvcRequestBuilders.post("/authentication")
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .with(csrf())
                .param("username", ユーザーアカウント)
                .param("password", パスワード)
        );
        result.andExpect(status().isOk())
                .apply(springSecurity()) // これを apply しないと csrf を設定しても 404 になってしまう。
                .andExpect(forwardedUrl("/hoge/list"));
    }
}
```


## formの設定について
[公式のドキュメント](https://spring.io/blog/2014/05/23/preview-spring-security-test-web-security) にも csrf() への言及はありますが、明示的に post 要求せず、下記の実装である場合に、 username と password がサーバー側に渡りませんでした。何か問題はあるのでしょうが、わからず。。。

```TestClass.java
public class TestClass {
    public パラメーターが渡らないメソッド() {
        ResultActions result2 = mvc.perform(formLogin("/authentication")
                .user(ユーザーアカウント).password(パスワード));
        result2.andExpect(status().isOk())
                .andExpect(forwardedUrl("/clients/list"))
                .andDo(MockMvcResultHandlers.print());
        ResultActions result3 = mvc.perform(MockMvcRequestBuilders.post("/authentication")
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .with(csrf())
                .with(user(ユーザーアカウント).password(パスワード)));
        result3.andExpect(status().isOk())
                .andExpect(forwardedUrl("/clients/list"))
                .andDo(MockMvcResultHandlers.print());
    }
}
```

csrf().asHeader() でも結果は同じでした。

## springSecurity の適用
springSecurity() を適用させないと、MockMvcRequestBuilders#post　に　csrf() を設定しても 404 になります。