【问题标题】:Testing CORs in SpringBootTest在 SpringBootTest 中测试 COR
【发布时间】:2017-07-24 03:25:37
【问题描述】:

我正在尝试编写一个测试来验证我们的 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;
    }

我已通过调试器验证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 标头。

任何帮助表示赞赏。

【问题讨论】:

  • 我认为您应该在创建模拟 GET 请求时将 http Origin 标头添加到要测试的值中。我怀疑 Spring 不会处理您对没有来源的 CORS 的请求。
  • 是的,我今天早上知道了。如果你把它写成答案我会接受。

标签: spring-mvc spring-boot testing cors


【解决方案1】:

CORS 请求需要包含一个Origin 标头,以便服务器处理它。模拟 GET 请求没有此标头。 API 确实允许我们在模拟请求中包含标头。

公共 MockHttpServletRequestBuilder 标头(字符串名称, 对象...值)

向请求添加标头。总是添加值。 参数: name - 标题名称 values - 一个或多个标头值

这是有效的代码

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

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

【讨论】:

  • 用一些示例代码更新了您的答案。我还需要指定Access-Control-Request-Method
【解决方案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!

【讨论】:

    【解决方案3】:

    为 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 主应用程序类包含一个注册 CORS 过滤器类的 FilterRegistrationBean。这是我的主要应用程序类的示例:

    @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获得更多信息

    【讨论】:

      【解决方案4】:

      这就是我所做的。我已经使用 Configuration @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 - Forbidden 响应。

      【讨论】:

        【解决方案5】:

        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";
        }
        
        

        【讨论】:

          猜你喜欢
          • 2019-09-15
          • 2017-05-28
          • 2018-02-07
          • 2020-01-10
          • 2019-01-17
          • 1970-01-01
          • 2021-04-12
          • 1970-01-01
          • 2019-11-04
          相关资源
          最近更新 更多