在SpringBootTest中测试COR

时间:2017-03-03 21:25:08

标签: spring-mvc spring-boot testing cors

我正在尝试编写测试以验证我们的CORS设置。我这样配置CORS。

@Override
    public void configure(HttpSecurity httpSecurity) throws Exception {
//        super.configure(httpSecurity);
        httpSecurity.csrf().disable();
        httpSecurity.authorizeRequests().antMatchers("/**").permitAll();
        httpSecurity.cors();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        List<String> allowedMethods = CORS_ALLOWED_METHODS;
        configuration.setAllowedMethods(allowedMethods);
        configuration.setAllowedOrigins(CORS_ALLOWED_ORIGINS);
        configuration.setAllowedHeaders(CORS_ALLOWED_HEADERS);
        configuration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

我已经通过调试器验证了{test}运行时CORS_ALLOWED_METHODS的值。

这是我的测试。当我在标题上断言时它失败了。

@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("dev")
public class ControllerTests {

    @Autowired
    private WebApplicationContext wac;

    public MockMvc mockMvc;

    @Before
    public void setup() {
        DefaultMockMvcBuilder builder = MockMvcBuilders
                .webAppContextSetup(this.wac)
                .apply(SecurityMockMvcConfigurers.springSecurity())
                .dispatchOptions(true);
        this.mockMvc = builder.build();
    }

    @Test
    public void testCors() throws Exception {
        this.mockMvc
                .perform(get("/test-cors"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(header().string("Access-Control-Allow-Methods", "OPTIONS,GET,HEAD,POST,PUT,DELETE,TRACE,CONNECT"))
                .andExpect(content().string("whatever"));
    }

    @SpringBootApplication(scanBasePackages = {"the.packages"})
    @Controller
    static class TestApplication {

        public static void main(String[] args) throws Exception {
            SpringApplication.run(TestApplication.class, args);
        }

        @RequestMapping(value = {"test-cors"},  method = RequestMethod.GET)
        @ResponseStatus(HttpStatus.OK)
        public @ResponseBody String testCors() {
            return "whatever";
        }
    }
}

请注意,当我实际运行具有此配置的SpringBoot应用程序时,我们会获得CORS标头。

任何帮助表示感谢。

5 个答案:

答案 0 :(得分:10)

CORS请求需要包含Origin标头,供服务器处理。模拟GET请求没有此标头。 API允许我们在模拟请求中包含标题。

  
    

public MockHttpServletRequestBuilder header(String name,                                                 对象...值)

         

为请求添加标头。始终添加值。     参数:     name - 标题名称     值 - 一个或多个标题值

  

以下是适用的代码

.perform(options("/test-cors")
    .header("Access-Control-Request-Method", "GET")
    .header("Origin", "http://www.someurl.com"))

两个标头都是必需的,configuration要求允许的来源和方法与测试中传递的值一致。

答案 1 :(得分:2)

而不是初始化CorsConfigurationSource Bean简单地直接初始化CorsFilter。只需改变这种方法并尝试,

@Bean
public CorsFilter corsFilter() {
        CorsConfiguration configuration = new CorsConfiguration();
        List<String> allowedMethods = CORS_ALLOWED_METHODS;
        configuration.setAllowedMethods(allowedMethods);
        configuration.setAllowedOrigins(CORS_ALLOWED_ORIGINS);
        configuration.setAllowedHeaders(CORS_ALLOWED_HEADERS);
        configuration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return new CorsFilter(source);
}

HTH!

答案 2 :(得分:0)

为CORS设置编写测试的另一种方法是在预定义的url上执行模拟请求,然后断言其MockHttpServletResponse。这是有效的代码 对我来说:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("corsFilterBean")
public class CORSFilterTests {

@Autowired
private MockMvc mvc;

@Test
public void test_corsFilterBean() throws Exception {

    MvcResult result = mvc
            .perform(get("/users/all"))
            .andExpect(status().isOk())
            .andExpect(header().string("Access-Control-Allow-Origin", "*"))
            .andExpect(header().string("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE"))
            .andExpect(header().string("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Auth-Token, X-Csrf-Token, WWW-Authenticate, Authorization"))
            .andExpect(header().string("Access-Control-Expose-Headers", "custom-token1, custom-token2"))
            .andExpect(header().string("Access-Control-Allow-Credentials", "false"))
            .andExpect(header().string("Access-Control-Max-Age", "3600"))
            .andDo(print())
            .andReturn();

    MockHttpServletResponse mockResponse = result.getResponse();

    assertThat(mockResponse.getContentType()).contains("application/json;charset=UTF-8");

    Collection<String> responseHeaders = mockResponse.getHeaderNames();
    assertThat(responseHeaders).isNotNull();
    assertThat(1).isEqualTo(1);
    assertThat(responseHeaders.size()).isBetween(5, 15);
  }
}

这假定有一个CORS规范类可以启用跨源请求。 这是我的CORS过滤器类的示例:

public class CORSFilterss extends GenericFilterBean implements Filter {

private Logger logger = LoggerFactory.getLogger(this.getClass());

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {

    HttpServletResponse httpResponse = (HttpServletResponse) response;
    httpResponse.setHeader("Access-Control-Allow-Origin", "*");
    httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
    httpResponse.setHeader("Access-Control-Allow-Headers",
            "Origin, X-Requested-With, Content-Type, Accept, X-Auth-Token, X-Csrf-Token, WWW-Authenticate, Authorization");
    httpResponse.setHeader("Access-Control-Expose-Headers", "custom-token1, custom-token2");
    httpResponse.setHeader("Access-Control-Allow-Credentials", "false");
    httpResponse.setHeader("Access-Control-Max-Age", "3600");

    StringBuilder sb = new StringBuilder();
    sb.append("\nCORS HEADERS:\n");
    sb.append("---------------\n");
    httpResponse.getHeaderNames()
            .forEach(name -> {
                        sb.append(name).append(": ").append(httpResponse.getHeader(name)).append("\n");
                    }
            );
    logger.debug("********** CORS Configuration Completed **********");
    logger.debug(sb.toString());

    chain.doFilter(request, response);
  }
} 

此外,spring boot主应用程序类包含一个 FilterRegistrationBean ,它注册CORS过滤器类。这是我主要应用课程的样本:

@SpringBootApplication
public class SpringTestingApplication implements CommandLineRunner {

public static void main(String[] args) {
    SpringApplication.run(SpringTestingApplication.class, args);
}
@Override
public void run(String... args) throws Exception {

}
@Bean
public FilterRegistrationBean corsFilterRegistration() {
    FilterRegistrationBean registrationBean =
            new FilterRegistrationBean(new CORSFilterss());
    registrationBean.setName("CORS FILTER");
    registrationBean.addUrlPatterns("/*");
    registrationBean.setOrder(1);

    return registrationBean;

  }
}

您可以从此github demo app

获取更多信息

答案 3 :(得分:0)

这就是我所做的。我已经使用配置@Configuration来使用Springboot启用CORS。这是我的课程:

在applicatoin.properties文件中,您可以如下添加属性和域:

  

allowed.origins = .someurl.com, .otherurl.com,*。someotherurl.com

您的配置类:

  

@配置     @EnableWebMvc     公共类AppConfig扩展了WebMvcConfigurerAdapter {

private static final Logger logger = LoggerFactory.getLogger(AppConfig.class);

@Value("#{'${allowed.origins}'.split(',')}")
private List<String> rawOrigins;

@Bean
public RestTemplate restTemplate() {
    return new RestTemplate();
}

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurerAdapter() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            logger.info("Adding CORS to the APP");
            registry.addMapping("/**")
                        .allowedOrigins(getOrigin())
                    .allowedMethods(HttpMethod.GET.name(), HttpMethod.POST.name(), HttpMethod.OPTIONS.name())
                    .allowedHeaders(HttpHeaders.AUTHORIZATION, HttpHeaders.CONTENT_TYPE, "accessToken", "CorrelationId", "source")
                    .exposedHeaders(HttpHeaders.AUTHORIZATION, HttpHeaders.CONTENT_TYPE, "accessToken", "CorrelationId", "source")
                    .maxAge(4800);
        }
        /**
         * This is to add Swagger to work when CORS is enabled
         */
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {

               registry.addResourceHandler("swagger-ui.html")
                        .addResourceLocations("classpath:/META-INF/resources/");

                registry.addResourceHandler("/webjars/**")
                        .addResourceLocations("classpath:/META-INF/resources/webjars/");

        }
    };
}


public String[] getOrigin() {
    int size = rawOrigins.size();
    String[] originArray = new String[size];
    return rawOrigins.toArray(originArray);
}

}

这是我对控制器的测试课程:

@RunWith(SpringRunner.class)
@WebMvcTest(AuthController.class)
@AutoConfigureMockMvc(secure=false)
@SuppressWarnings("unchecked")
public class AuthControllerTest {

public static final String EXPIRES_IN = "expiresIn";
public static final String TOKEN_TYPE = "tokenType";
public static final String SCOPE = "scope";
public static final String ID_TOKEN = "idToken";
@Autowired
private MockMvc mockMvc;

@MockBean
private AuthService service;

@Mock
private WebMvcConfigurer corsConfigurer;

private static final ObjectMapper objectMapper = new ObjectMapper();

private static final String RESPONSE_JSON = "{\"response\":\"result\"}";

private static final String REDIRECT_URL = "url";

private static final String CODE = "code";

private static final String STATE = "state";

private static final String ACCESS_TOKEN = "accessToken";

private static final Map<String, String> requestHeaders = new HashMap<>();

@Before
public void setup() {
        requestHeaders.put("source", "source");
        requestHeaders.put("CorrelationId", "CorrelationId");
}

/**
* Controller Test to pass the CORS
*/
@Test
public void testGetLoginUrl() throws Exception {
    when(service.getLoginUrl(anyString(),anyMap())).thenReturn(objectMapper.readTree(RESPONSE_JSON));
    this.mockMvc.perform(get("/vendor/auth/loginurl?redirect_url="+ REDIRECT_URL)
            .header("CorrelationId", "1234")
            .header("Origin", "http://localhost:8080"))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.response", is("result")));

}
}

请注意,在测试类中,我在模拟WebMvcConfigurer,它将在内部添加CORS Registry并将其初始化为您的SpringTest

但是,您需要在标头中传递Origin;否则,您将从控制器中收到 403-禁止访问响应。

答案 4 :(得分:-2)

springboot 2.0

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("*")
                .maxAge(3600);
    }

@CrossOrigin(origins="http://localhost:9000", maxAge=3600)
@RestController
public class RestController {}

@CrossOrigin(origins="http://localhost:9000")
@GetMapping("/hello")
public Greeting greeting() {
    return "world";
}