Spring MVC - ошибка 404 с @EnableGlobalMethodSecurity при тестировании контроллера с использованием @WebMvcTest

Мы пытаемся добавить модульное тестирование в наш проект Spring Controllers (Кстати, тесты интеграции работают нормально), но мы страдаем очень странным поведением, когда мы добавляем конфигурацию с @EnableGlobalMethodSecurity (с аннотациями JSR-250), если Контроллер реализует интерфейс (любой интерфейс), Controller не входит в контекст приложения Spring как "обработчик запросов" (я проверял это методом: org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.processCandidateBean(String beanName)), то есть сопоставления запросов, определенные в Controler (@PostMapping,...) не регистрируются как потенциальные местоположения, но если интерфейс удален, то Контроллер и путь будут найдены без проблем.

Это мой контроллер (упрощенный) с простым интерфейсом MyInterface:

@RestController
@RequestMapping(path = "/api/customer")
@RolesAllowed({Role.Const.ADMIN})
public class CustomerController implements MyInterface {    
    @Override // The only method in MyInterface 
    public void myMethod(Object param) throws QOException {
        System.out.println("Hello");
    }    
    @PostMapping(path = {"/", ""})
    public Customer create(@RequestBody Customer data) throws QOException {
        return customerService.create(data);
    }
}

А это тестовый класс (если я удалю @EnableGlobalMethodSecurity config все работает нормально):

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = CustomerController.class)
@Import({ CustomerController.class, TestAuthConfiguration.class, TestAuthConfiguration2.class})  //, TestAuthConfiguration.class })
@ActiveProfiles({"test", "unittest"})
@WithMockUser(username = "test", authorities = { Role.Const.ADMIN })
class CustomerControllerTest {
    private static final Logger LOG = LogManager.getLogger(CustomerControllerTest.class);
    @EnableWebSecurity
    protected static class TestAuthConfiguration extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            LOG.info("configure(HttpSecurity http) ");
            http.csrf().disable().authorizeRequests().filterSecurityInterceptorOncePerRequest(true)
                    .antMatchers("/api/session/**").permitAll() //
                    .antMatchers("/api/**").authenticated() //
                    .anyRequest().permitAll().and() //
                    .addFilterBefore(new OncePerRequestFilter() {
                        @Override
                        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                FilterChain filterChain) throws ServletException, IOException {
                            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
                            if (auth != null) {
                                LOG.info("User authenticated: {}, roles: {}", auth.getName(), auth.getAuthorities());
                            }
                            filterChain.doFilter(request, response);
                        }
                    }, BasicAuthenticationFilter.class).sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }

    }

    @EnableGlobalMethodSecurity(jsr250Enabled = true, securedEnabled = false)
    protected static class TestAuthConfiguration2 extends GlobalMethodSecurityConfiguration {
        @Bean
        public GrantedAuthorityDefaults grantedAuthorityDefaults() {
            return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix
        }
    }

    @Autowired
    MockMvc mockMvc;

    @Test
    void testCreate() throws Exception {
        Customer bean = new Customer();
        bean.setName("Test company");           
        when(customerServiceMock.create(bean)).thenReturn(bean);
        mockMvc.perform(post("/api/customer") 
        .content(JsonUtils.toJSON(bean)).contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.jsonPath("$.name").value("Test company"));
    }
}

Я не понимаю, что происходит, я пытался найти пример модульного тестирования в контроллерах с безопасностью на основе аннотаций JSR-250 (@RollesAllowed), но ничего полезного не нашел, все равно эта проблема звучит (чтобы меня) к ошибке, но я не уверен, поэтому любая помощь приветствуется.

Версии библиотек:

  • Версия Spring Boot: 2.2.2
  • Ядро пружины: 5.2.1
  • Mockito Core: 3.1.0

1 ответ

Попробуйте установить proxyTargetClass приписывать true. Я столкнулся с аналогичной проблемой и решил ее, добавив это.

@EnableGlobalMethodSecurity(jsr250Enabled = true, proxyTargetClass = true)

Также я бы рекомендовал прочитать javadocs для EnableGlobalMethodSecurity особенно обращая внимание на proxyTargetClass а также mode атрибуты.

Другие вопросы по тегам