跨域问题产生的根本原因是浏览器的同源策略。同一个源
,即同一协议/主机/端口元组的资源加载不会被限制。
根据同源定义,不同端口或不同子域名的资源加载均会被同源策略拦截。其中,子域名页面脚本中可通过设置当前域名来规避:
1
| document.domain = "company.com";
|
如果确实是跨源了,则需要通过跨源资源共享(CORS):
预检请求
预检请求是指在发起真正数据请求前,发起一个OPTIONS请求确认是否允许跨域,以避免跨域请求对服务器的用户数据产生未预期的影响。
简单请求,复杂请求
可以简单的认为,没有预检请求的称为简单请求,反之则是复杂请求。
简单请求跨域访问
使用 Origin 和 Access-Control-Allow-Origin就可以。服务端响应Header:
1 2
| Access-Control-Allow-Origin: * Origin:
|
复杂请求跨域访问
复杂请求的问题,主要是针对预检请求的处理:
- 返回204状态码
- 需要跨源Header :
Access-Control-Allow-Origin
以下是一次POST请求示例:
以上示例中,重点为预检请求的服务端响应:1 2 3 4 5
| Access-Control-Allow-Origin: https://foo.example Access-Control-Allow-Methods: POST, GET, OPTIONS Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
|
1. web新增Filter处理跨域
springboot项目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Bean public CorsFilter corsFilter() { final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); final CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowCredentials(true); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); return new CorsFilter(urlBasedCorsConfigurationSource); }
|
spring项目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| CORSFilter implements Filter ..... @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse;
String origin = request.getHeader("Origin"); response.setHeader("Access-Control-Allow-Origin", origin); response.setHeader("Access-Control-Allow-Methods", "GET,OPTIONS,POST,HEAD,PUT,DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "Accept,Origin,X-Requested-With,Content-Type,X-Auth-Token,content-type,Authorization"); response.setHeader("Access-Control-Allow-Credentials", "true");
response.addHeader("x-frame-options","SAMEORIGIN");
if (request.getMethod().equalsIgnoreCase("OPTIONS")) { response.setStatus(HttpServletResponse.SC_NO_CONTENT); return; }
chain.doFilter(servletRequest, servletResponse);
}
|
2. nginx配置处理跨域
1 2 3 4 5 6 7 8 9 10 11
| 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,X-Auth-Token,Authorization'; add_header Access-Control-Max-Age 86400; if ($request_method = 'OPTIONS') { return 204; } }
|
预检请求携带证书问题
CORS 预检请求不能包含凭据。预检请求的响应必须指定 Access-Control-Allow-Credentials: true
来表明可以携带凭据进行实际的请求。
附带身份凭证cookie的跨域请求
- 首先允许
Access-Control-Allow-Headers
中必须要有该header名称,特别是自定义的。
- 服务端的
Access-Control-Allow-Origin
不能设置为*
,而应将其设置为特定的域。比如在Filter中:1 2
| String origin = request.getHeader("Origin"); response.setHeader("Access-Control-Allow-Origin", origin);
|
- 服务端
Access-Control-Allow-Headers
不能为*
- 服务端
Access-Control-Allow-Methods
不能为*
参考 MSDN CORS