如何在 Spring Boot 中测试 CORS?当我在 MockMvcBuilders 中尝试时,尽管 Origin 错误,它总是返回 200

Posted

技术标签:

【中文标题】如何在 Spring Boot 中测试 CORS?当我在 MockMvcBuilders 中尝试时,尽管 Origin 错误,它总是返回 200【英文标题】:How test CORS in Spring Boot? When I try in MockMvcBuilders, it always returns 200 despite Origin being wrong 【发布时间】:2018-03-22 12:14:42 【问题描述】:

以下测试(我包括两个类以查看其中一个是否可以工作)都没有调用控制器的问题。我希望它拒绝 CORS 问题,因为我没有添加任何 CORS 配置。 (然后我想使用 CORS 配置进行测试并通过)。

如何强制 CORS 失败?

第一次尝试:

import com.testing.Application;
import com.testing.config.ControllerConfig;
import com.testing.controller.MyController;
import com.testing.dto.TestDateResponse;
import com.testing.exception.GlobalExceptionHandler;
import com.testing.service.TestService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.ConfigurableWebApplicationContext;

import java.time.LocalDate;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
@WebAppConfiguration
public class TestCORS

    @Autowired
    private ConfigurableWebApplicationContext context;

    private MockMvc mockMvc;
    private ObjectMapper objectMapper;

    @InjectMocks
    private MyController myController;

    @Autowired
    private RestTemplate restTemplate;

    @Before
    public void setup()
    
        //Initialize our injected mocks
        MockitoAnnotations.initMocks(this);

        //Create a controller
        myController = new MyController( new TestService(), restTemplate );

        //Create an environment for it
        mockMvc = MockMvcBuilders
            .webAppContextSetup(context)
            .dispatchOptions(true)
            .build();

        //Create our marshaller
        objectMapper = new ObjectMapper();
    

    /**
     * Tests that we fail when trying to access cross origin
     * @throws Exception if json unmarshaller cannot parse the response
     */
    @Test
    public void testValidRequest() throws Exception
    
        String request = "\"asOfDate\":\"20170210\"";

        //Call to test a date
        ResultActions actions = mockMvc.perform(
            post("/v1/testdate")
                .contentType(MediaType.APPLICATION_JSON)
                .content(request)

                //CORS HEADERS
                .header("Access-Control-Request-Method", "DELETE")
                .header("Origin", "https://evil.com")
        );

        actions.andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8));

        TestDateResponse response = objectMapper.readValue(actions.andReturn().getResponse().getContentAsString(), TestDateResponse.class);
        assertThat(response, notNullValue());
        // verify date has returned back correctly
        assertThat(response.getRetDate(), equalTo(LocalDate.of(2017, 02, 10)));
    

第二次尝试:

import com.testing.config.ControllerConfig;
import com.testing.controller.MyController;
import com.testing.dto.TestDateResponse;
import com.testing.exception.GlobalExceptionHandler;
import com.testing.service.TestService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDate;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ControllerConfig.class)
public class TestCORS

    private MockMvc mockMvc;
    private ObjectMapper objectMapper;
    private MyController myController;

    @Autowired
    private RestTemplate restTemplate;

    @Before
    public void setup()
    
        //Initialize our injected mocks
        MockitoAnnotations.initMocks(this);

        //Create a controller
        myController = new MyController( new TestService(), restTemplate );

        //Create an environment for it
        mockMvc = MockMvcBuilders.standaloneSetup(myController)
            .setControllerAdvice(new GlobalExceptionHandler())
            .build();

        //Create our marshaller
        objectMapper = new ObjectMapper();
    

    /**
     * Tests that we fail when trying to access cross origin
     * @throws Exception if json unmarshaller cannot parse the response
     */
    @Test
    public void testValidRequest() throws Exception
    
        String request = "\"asOfDate\":\"20170210\"";

        //Call to test a date
        ResultActions actions = mockMvc.perform(
            post("/v1/testdate")
                .contentType(MediaType.APPLICATION_JSON)
                .content(request)

                //CORS HEADERS
                .header("Access-Control-Request-Method", "GET")
                .header("Origin", "http://www.someurl.com")
        );
        actions.andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8));

        TestDateResponse response = objectMapper.readValue(actions.andReturn().getResponse().getContentAsString(), TestDateResponse.class);
        assertThat(response, notNullValue());
        // verify date has returned back correctly
        assertThat(response.getRetDate(), equalTo(LocalDate.of(2017, 02, 10)));
    

【问题讨论】:

【参考方案1】:

CORS 不能那样工作。

要检查 CORS,必须对 URL 进行预检调用。 这不是 POST,而是针对具有 CORS 标头的相同 URL 的 OPTIONS 请求。

无论是否允许实际调用(针对 DELETE),通过该调用,您将收到 CORS 响应。

类似的东西应该可以工作:

    ResultActions actions = mockMvc.perform(
        options("/v1/testdate")    
            .contentType(MediaType.APPLICATION_JSON)
            //CORS HEADERS
            .header("Access-Control-Request-Method", "DELETE")
            .header("Origin", "https://evil.com")
    );

然后简单地断言预期的响应标头。

【讨论】:

我收到此错误:javax.servlet.ServletException: No adapter for handler [org.springframework.web.servlet.handler.AbstractHandlerMapping$PreFlightHandler@82c57b3]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler 您是否在 RequestController 或 RequestMapping 上配置了跨源?例如与:@CrossOrigin(origins = "not-evil.com") 不,我不知道。我需要那个吗?这会修复这个错误吗?有没有办法让自定义适配器 bean 摆脱这个错误? 这超出了我的知识范围。

以上是关于如何在 Spring Boot 中测试 CORS?当我在 MockMvcBuilders 中尝试时,尽管 Origin 错误,它总是返回 200的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring Boot 中使用 Spring Security 启用 CORS

如何在 Spring Boot + Spring Security 应用程序中配置 CORS?

如何在 Spring Boot 和 Angular 中启用 CORS

如何在 Spring Boot 安全性中禁用 CORS

如何在 Spring Boot 安全性中禁用 CORS

如何在 Spring Boot API 中停用 CORS(版本 2.2.6)