一 问题
后端的API,直接通过IP+端口访问没问题,直接访问域名也OK。通过别的域名跨域访问就异常了。
console也明确提示:
二 修改
以下为springboot bean注入方式。
新增拦截器代码,
public class CORSFilter extends GenericFilterBean implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chaine)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Origin", "www.a.com");
httpResponse.setHeader("Access-Control-Allow-Methods", "PUT, POST, GET, OPTIONS, DELETE");
httpResponse.setHeader("Access-Control-Allow-Headers", "content-type, authorization");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
System.out.println("****************** CORS Configuration Completed *******************");
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if (httpServletRequest.getMethod().equals("OPTIONS"))
httpResponse.setStatus(HttpServletResponse.SC_OK);
chaine.doFilter(request, response);
}
}
再原来的拦截器配置增加:注意setorder 数字越小优先级越高。这个放在@Configuration 类里面。
@Bean
public FilterRegistrationBean crossFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(new CORSFilter());
ArrayList<String> urls = new ArrayList<>();
urls.add("/*");//配置过滤规则
registrationBean.setUrlPatterns(urls);
registrationBean.setOrder(1);
logger.info("cross filer over");
return registrationBean;
}
NG配置:
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
}
一定要注意OPTIONS配置。
我们之前报错,就是NG先给拦住了,校验非正常请求直接403.
注意:
前端同学也得配合检查下请求方式:
$.ajax({
url: "https://b.com/method",
type: 'POST',
data: requestParams,
dataType: 'json',
xhrFields: {
withCredentials: true
},
crossDomain: true,
async: false,
...
容易出问题的地方,url 只写path,不写明HTTP协议。参数不加上跨域请求。
三 原理
通常问题就会解决,这个问题折腾了大半天。看了下Mozilla浏览器对此的官方解释:
前端Web中有两个域名,a.com和b.com,其中a.com是访问主站(页面),b.com是数据提交接口的服务器(XHR请求)
普通的GET请求:
但是跨域的POST请求,就多了一步
这里涉及预检请求(preflight request)
跨域资源共享(CORS)标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源。另外,规范要求,
对那些可能对服务器数据产生副作用的HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求)
,浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)Preflighted requests
Unlike “simple requests” (discussed above), for "preflighted" requests the browser first sends an HTTP request using the
OPTIONS
method to the resource on the other origin, in order to determine if the actual request is safe to send. Cross-site requests are preflighted like this since they may have implications to user data.
CORS规定,Content-Type不属于以下MIME类型的,都属于预检请求。所以 application/json的请求 会在正式通信之前,增加一次"预检"请求,会带上头部信息。
The
Access-Control-Request-Method
header notifies the server as part of a preflight request that when the actual request is sent, it will be sent with aPOST
request method. TheAccess-Control-Request-Headers
header notifies the server that when the actual request is sent, it will be sent with aX-PINGOTHER
andContent-Type
custom headers. The server now has an opportunity to determine whether it wishes to accept a request under these circumstances.Lines 13 - 22 above are the response that the server sends back, which indicate that the request method (
POST
) and request headers (X-PINGOTHER
) are acceptable. In particular, let's look at lines 16-19:Access-Control-Allow-Origin: http://foo.example Access-Control-Allow-Methods: POST, GET, OPTIONS Access-Control-Allow-Headers: X-PINGOTHER, Content-Type Access-Control-Max-Age: 86400The server responds with
Access-Control-Allow-Origin: http://foo.example
, restricting access to just the requesting origin domain. It also responds withAccess-Control-Allow-Methods
, which says thatPOST
andGET
are viable methods to query the resource in question (this header is similar to theAllow
response header, but used strictly within the context of access control).The server also sends
Access-Control-Allow-Headers
with a value of "X-PINGOTHER, Content-Type
", confirming that these are permitted headers to be used with the actual request. LikeAccess-Control-Allow-Methods
,Access-Control-Allow-Headers
is a comma separated list of acceptable headers.
The server 响应信息,头部 也得包含允许的Access-Control-Allow-Origin, 我测试的时候传”*“不行,得加具体的domain.就是a.com.
还有Access-Control-Allow-Headers 等,具体见上面代码,表示接受非默认的的Content-Type。