spring 由于标准标头,CORS 预检请求失败
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38507370/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
CORS preflight request fails due to a standard header
提问by joanlofe
While debugging a CORS issue I am experiencing I've found the following behaviour. Chrome makes the following OPTIONS preflight request (rewritten in CURL by Chrome itself):
在调试我遇到的 CORS 问题时,我发现了以下行为。Chrome 发出以下 OPTIONS 预检请求(由 Chrome 本身在 CURL 中重写):
curl -v 'https://www.example.com/api/v1/users' -X OPTIONS -H 'Access-Control-Request-Method: POST' -H 'Origin: http://example.com' -H 'Accept-Encoding: gzip,deflate,sdch' -H 'Accept-Language: es-ES,es;q=0.8,en;q=0.6' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36' -H 'Accept: */*' -H 'Referer: http://example.com/users/new' -H 'Connection: keep-alive' -H 'Access-Control-Request-Headers: accept, x-api-key, content-type'
The response from the server to this request if the following:
如果满足以下条件,则服务器对此请求的响应:
< HTTP/1.1 403 Forbidden
< Date: Thu, 21 Jul 2016 14:16:56 GMT
* Server Apache/2.4.7 (Ubuntu) is not blacklisted
< Server: Apache/2.4.7 (Ubuntu)
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< Strict-Transport-Security: max-age=31536000 ; includeSubDomains
< X-Frame-Options: SAMEORIGIN
< Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
< Content-Length: 20
< Keep-Alive: timeout=5, max=100
< Connection: Keep-Alive
being the body of the response 'Invalid CORS request'. If I repeat the request removing the header 'Access-Control-Request-Method' (and only that header) the OPTIONS requests succeeds with the following reponse:
作为响应“无效的 CORS 请求”的正文。如果我重复删除标头“Access-Control-Request-Method”(并且仅该标头)的请求,则 OPTIONS 请求会成功并显示以下响应:
< HTTP/1.1 200 OK
< Date: Thu, 21 Jul 2016 14:21:27 GMT
* Server Apache/2.4.7 (Ubuntu) is not blacklisted
< Server: Apache/2.4.7 (Ubuntu)
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< Strict-Transport-Security: max-age=31536000 ; includeSubDomains
< X-Frame-Options: SAMEORIGIN
< Access-Control-Allow-Headers: origin, content-type, accept, x-requested-with, x-api-key
< Access-Control-Max-Age: 60
< Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
< Access-Control-Allow-Origin: *
< Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
< Content-Length: 0
< Keep-Alive: timeout=5, max=100
< Connection: Keep-Alive
However, the offending header is a CORS spec standard headerso it should not prevent the request from succeeding, right? Why is this header causing such behaviour?
但是,违规标头是CORS 规范标准标头,因此它不应阻止请求成功,对吗?为什么这个标题会导致这种行为?
And how can I tweak the access control headers sent by my server to make the request work when made with Chrome?
以及如何调整服务器发送的访问控制标头以使请求在使用 Chrome 时正常工作?
By the way, I am using Chrome 36.0, and the server is using Spring Boot, with the CORS headers being managed by Spring.
顺便说一下,我使用的是 Chrome 36.0,服务器使用的是 Spring Boot,CORS 标头由 Spring 管理。
When the request is made by Firefox (v47.0) the behaviour is different but with an analogue result. Firefox does not even send the preflight request, it directly sends the POST request, which receives as response a 403 Forbidden. However, if I copy the request with the 'Copy as cURL' option, and repeat it from a terminal window, It succeeds and sends the correct CORS headers in the response.
当请求由 Firefox (v47.0) 发出时,行为不同,但结果类似。Firefox 甚至不发送预检请求,它直接发送 POST 请求,该请求接收 403 Forbidden 作为响应。但是,如果我使用“Copy as cURL”选项复制请求,并从终端窗口重复它,它会成功并在响应中发送正确的 CORS 标头。
Any idea?
任何的想法?
Update: Firefox does send the preflight OPTIONS request (as shown by the Live HTTP headers plugin), but Firebug masks it, so the behaviour in both browsers it exactly the same. In both browsers is the 'Access-control-request-method' header the difference that makes the request fail.
更新:Firefox 确实发送了预检选项请求(如 Live HTTP 标头插件所示),但 Firebug 屏蔽了它,因此两个浏览器中的行为完全相同。在这两种浏览器中,'Access-control-request-method' 标头是导致请求失败的差异。
回答by joanlofe
After a lot of struggling, I finally found the problem. I configured a request mapping in Spring to handle OPTIONS traffic, like this:
经过一番折腾,终于找到了问题所在。我在 Spring 中配置了一个请求映射来处理 OPTIONS 流量,如下所示:
@RequestMapping(value= "/api/**", method=RequestMethod.OPTIONS)
public void corsHeaders(HttpServletResponse response) {
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.addHeader("Access-Control-Allow-Headers", "origin, content-type, accept, x-requested-with");
response.addHeader("Access-Control-Max-Age", "3600");
}
I did not know that by default Spring uses a default CORS processor, and it seems it was interfering with my request mapping. Deleting my request mapping and adding the @CrossOriginannotation to the appropriate request mappings solved the problem.
我不知道默认情况下 Spring 使用默认的 CORS 处理器,它似乎干扰了我的请求映射。删除我的请求映射并将@CrossOrigin注释添加到适当的请求映射解决了这个问题。
回答by Om Sharma
i also faced the same issue and find solution for enabling global cors issue in spring boot
我也遇到了同样的问题,并找到了在 spring boot 中启用全局 cors 问题的解决方案
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE").allowedOrigins("*")
.allowedHeaders("*");
}
}
after this , we need to enable CORS in spring security level also, so for this add cors() in your SecurityConfiguration class which extent WebSecurityConfigurerAdapter
在此之后,我们还需要在 spring 安全级别启用 CORS,因此为此在您的 SecurityConfiguration 类中添加 cors() 扩展 WebSecurityConfigurerAdapter
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors()
.and()
.csrf().disable()
.authorizeRequests()..
}
回答by Alex Elkin
I had the same issue. I've resolve it by adding 'OPTIONS' to allowed CORS methods in my Spring MVC configuration.
我遇到过同样的问题。我通过在 Spring MVC 配置中向允许的 CORS 方法添加“选项”来解决它。
@Configuration
@EnableWebMvc
@ComponentScan
public class RestApiServletConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
super.addCorsMappings(registry);
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000", "http://localhost:8080")
.allowedMethods("GET", "PUT", "POST", "DELETE", "OPTIONS");
}
}
回答by Stijn Van Bael
Edit: Enable CORS in security configuration and make sure options requests bypass security
编辑:在安全配置中启用 CORS 并确保选项请求绕过安全性
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// More security configuration here
}
回答by Prabu M
For me I have added @crossorigin
annotation in each of controller api call.
对我来说,我@crossorigin
在每个控制器 api 调用中添加了注释。
@CrossOrigin
@PostMapping(path = "/getListOfIndividuals", produces = { "application/json" }, consumes = { "application/json" })
public ResponseEntity<String> test(@RequestBody String viewIndividualModel)
throws Exception {
String individualDetails = globalService.getIndividualDetails(viewIndividualModel);
finalString = discSpecAssmentService.getViewFormForDisciplineEvaluation( viewIndividualModel);
return new ResponseEntity<String>(finalString, HttpStatus.OK);
}
回答by Mike
I added this as an answer because I couldn't format it well for the top voted answer.
我将此添加为答案,因为我无法将其格式化为最高投票的答案。
I found this post helpful as well: How to handle HTTP OPTIONS with Spring MVC?
我发现这篇文章也很有帮助:How to handle HTTP OPTIONS with Spring MVC?
DispatchServlet must be configured to pass along options request, or else it never reaches the mapped request:
DispatchServlet 必须配置为传递选项请求,否则它永远不会到达映射的请求:
...
<servlet>
<servlet-name>yourServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>dispatchOptionsRequest</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
...