概要
Spring Security にて下記の構成のフォーム認証のテストを実施した際に、404 エラーが発生しました。
POST
--------------------------------->
フォーム画面 AuthenticationProvider (認証処理)
・ユーザー名
・パスワード <=================================
ユーザー情報
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"));
}
}
Assertion の結果
java.lang.AssertionError: Status
Expected :200
Actual :404
回避策
下記 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の設定について
公式のドキュメント にも 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 になります。