Java 集成测试RestController时禁用或模拟Spring安全筛选器
在我的应用程序中,我在WebSecurity配置适配器扩展中添加了一个自定义过滤器:Java 集成测试RestController时禁用或模拟Spring安全筛选器,java,spring,spring-security,integration-testing,Java,Spring,Spring Security,Integration Testing,在我的应用程序中,我在WebSecurity配置适配器扩展中添加了一个自定义过滤器: @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { private static final RequestMatcher PROTECTED_URLS = new AntPathRequestMatcher("/v1/**"); @Overri
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final RequestMatcher PROTECTED_URLS = new AntPathRequestMatcher("/v1/**");
@Override
public void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(authenticationFilter(), AnonymousAuthenticationFilter.class)
.authorizeRequests()
.requestMatchers(PROTECTED_URLS)
.authenticated()
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable();
}
@Bean
AuthenticationFilter authenticationFilter() throws Exception {
final AuthenticationFilter filter = new AuthenticationFilter(PROTECTED_URLS);
// filter setup...
filter.setAuthenticationManager(authenticationManager());
return filter;
}
}
负责通过调用外部授权服务器验证访问令牌的筛选器本身定义为:
public class AuthenticationFilter extends AbstractAuthenticationProcessingFilter {
AuthenticationFilter(final RequestMatcher requiresAuth) {
super(requiresAuth);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse)
throws AuthenticationException, IOException, OAuth2Exception {
try {
// Get Authorization header.
String token = httpServletRequest.getHeader(AUTHORIZATION);
// Check if the token is valid by calling an external authorization server.
// Returns some Authentication if successful.
} catch (OAuth2Exception exception) {
// Return 401
} catch (Exception exception) {
// All other errors are 500s
}
}
@Override
protected void successfulAuthentication(final HttpServletRequest request,
final HttpServletResponse response,
final FilterChain chain,
final Authentication authResult)
throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authResult);
chain.doFilter(request, response);
}
}
@RestController
@RequestMapping(value = "/v1", produces = "application/json")
public class SomeController {
@Autowired
private SomeService someService;
@ResponseStatus(OK)
@PostMapping(value = "/a/path")
public SomeSuccessResponse pathHandlerMethod() {
return someService.someServiceMethod();
}
}
我尝试在控制器上执行集成测试,定义如下:
public class AuthenticationFilter extends AbstractAuthenticationProcessingFilter {
AuthenticationFilter(final RequestMatcher requiresAuth) {
super(requiresAuth);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse)
throws AuthenticationException, IOException, OAuth2Exception {
try {
// Get Authorization header.
String token = httpServletRequest.getHeader(AUTHORIZATION);
// Check if the token is valid by calling an external authorization server.
// Returns some Authentication if successful.
} catch (OAuth2Exception exception) {
// Return 401
} catch (Exception exception) {
// All other errors are 500s
}
}
@Override
protected void successfulAuthentication(final HttpServletRequest request,
final HttpServletResponse response,
final FilterChain chain,
final Authentication authResult)
throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authResult);
chain.doFilter(request, response);
}
}
@RestController
@RequestMapping(value = "/v1", produces = "application/json")
public class SomeController {
@Autowired
private SomeService someService;
@ResponseStatus(OK)
@PostMapping(value = "/a/path")
public SomeSuccessResponse pathHandlerMethod() {
return someService.someServiceMethod();
}
}
最后,我的测试设置如下所示:
@RunWith(SpringRunner.class)
@WebMvcTest(SomeController.class)
@Import(SecurityConfig.class)
@ContextConfiguration
@WebAppConfiguration
public class SomeControllerTest {
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private WebApplicationContext context;
@MockBean
private SomeService someService;
@Before
public void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity()) // When I comment out this line I'm getting 404 errors instead.
.build();
}
@Test
@WithMockUser
public void performIntegrationTest() throws Exception {
mockMvc.perform(post("/v1/a/path")).andExpect(status().isOk());
}
}
我希望在这种情况下关闭身份验证或以某种方式模拟身份验证—根本不应调用AuthenticationFilter
中的实际代码。为了实现这一点,在SomeControllerTest
类中,我尝试了:
- 用
@WithMockUser
- 使用
设置MockMvcBuilders
(请参阅上面的mockMvc
方法)使用setup()
和不使用它。应用(springSecurity())
- 用
注释@AutoConfigureMockMvc
类(同时将SomeControllerTest
和安全
参数设置为添加过滤器
)假
- 用
和@ContextConfiguration
注释@WebAppConfiguration
类(我不知道它是否改变了什么)SomeControllerTest
这些方法都没有禁用身份验证。当我运行测试时,调用外部服务的
AuthenticationFilter
的attemptAuthentication()
方法仍然被调用,这是我不希望发生的。禁用过滤器听起来与集成测试相矛盾,imho。您是否考虑过模拟过滤器
创建一个
public class MockAuthenticationFilter implements Filter {
// return mock data for different use cases.
}
然后在测试中注册此过滤器
@Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.apply(springSecurity(new MockAuthenticationFilter()))
.build();
}
这还允许您测试过滤器以某种方式工作的不同用例 谢谢你的回答。在实现了一个空的mock身份验证过滤器并将其附加到mockMvc之后,我现在一直得到404。