Отключение или насмешка фильтров Spring Security во время интеграционного тестирования RestController - PullRequest
0 голосов
/ 28 января 2020

В моем приложении добавлен пользовательский фильтр в расширение WebSecurityConfigurerAdapter:

@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();
    }
}

Наконец, моя настройка теста выглядит следующим образом:

@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
  • с настройкой mockMvc с помощью MockMvcBuilders (см. setup() метод выше) с .apply(springSecurity()) и без него
  • с аннотацией класса SomeControllerTest с @AutoConfigureMockMvc (для параметров secure и addFilters установлено false)
  • аннотируя класс SomeControllerTest с помощью @ContextConfiguration и @WebAppConfiguration (я не знаю, меняет ли это что-либо)

Ни один из этих подходов не отключает аутентификацию. Когда я запускаю тест, метод AuthenticationFilter attemptAuthentication(), вызывающий внешнюю службу, все еще вызывается, чего я не хочу.

1 Ответ

1 голос
/ 28 января 2020

Отключение фильтра звучит противоречиво для интеграционного теста, 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();

}

Это также позволит вам протестировать различные варианты использования, когда фильтр работает так или иначе.

...