Spring Securityで保護されたURLのアクセス制御のテストに苦労しています。
構成は次のようになります。
http
.authorizeRequests()
.antMatchers("/api/user/**", "/user").authenticated()
.antMatchers("/api/admin/**", "/templates/admin/**", "/admin/**").hasAuthority("ADMIN")
.anyRequest().permitAll();
そして、テストクラスは次のようになります:
package com.kubukoz.myapp;
import com.kubukoz.myapp.config.WebSecurityConfig;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import javax.transaction.Transactional;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {MyApplication.class, WebSecurityConfig.class})
@WebAppConfiguration
@TransactionConfiguration(defaultRollback = true)
@Transactional(rollbackOn = Exception.class)
public class MyApplicationTests {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@Autowired
private FilterChainProxy filterChainProxy;
@Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.dispatchOptions(true)
.addFilters(filterChainProxy)
.build();
}
@Test
public void testAnonymous() throws Exception {
mockMvc.perform(get("/api/user/account")).andExpect(status().is3xxRedirection());
}
@Test
public void testUserAccessForAccount() throws Exception{
mockMvc.perform(get("/api/user/account")).andExpect(status().isOk());
}
}
最後の2つのテストに合格する最も簡単な方法は何ですか? @WithMockUserが機能しませんでした。
FilterChainProxyを直接追加しないでください。代わりに、 参照 で示されるようにSecurityMockMvcConfigurers.springSecurity()
を適用する必要があります。以下に例を示します。
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
これの結果は:
FilterChainProxy
がFilter
としてMockMvc
に追加されます(以前と同様)。TestSecurityContextHolderPostProcessor
が追加されましたなぜTestSecurityContextHolderPostProcessor
が必要なのですか?その理由は、現在のユーザーをテストメソッドから作成されたMockHttpServletRequest
に伝達する必要があるためです。これは、Spring SecurityのSecurityContextRepositoryFilter
がSecurityContextHolder
の値をオーバーライドして、現在のSecurityContextRepository
(つまり、SecurityContext
内のHttpSession
)によって検出された値になるようにするために必要です。
更新
メソッド名にroleを含むものはすべて、渡された文字列の前に「ROLE_」が自動的に付加されます。
コメントに基づいて、問題はhasAuthorityの代わりにhasRoleを使用するように構成を更新する必要があることです(注釈がロールを使用しているため)。
.authorizeRequests()
.antMatchers("/api/user/**", "/user").authenticated()
.antMatchers("/api/admin/**", "/templates/admin/**", "/admin/**").hasRole("ADMIN")
.anyRequest().permitAll();
または
Spring Security 4.0.2+では、以下を使用できます。
@WithMockUser(authorities="ADMIN")
わかりました。
mockMvc.perform(get("/api/user/account")
.with(user("user")))
.andExpect(status().isOk());
それは今動作します。