跨域问题处理手册
什么是跨域?
在浏览器环境下,跨域是指一个域下的文档或脚本试图去请求另一个域下的资源。
域:由协议+域名+端口组成
正常情况下,如果我们通过ajax去请求另一个域下的资源时是不会成功的,浏览器默认会阻止这种行为,因为它违背了浏览器的同源策略。
同源策略(SOP: Same Origin Policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。
同源策略指 协议 + 域名 + 端口 三者相同,如果违背了同源策略那么浏览器将限制获取Cookie、LocalSgorage等、获取DOM和JS对象、发送Ajax请求。
跨域报错对照表
Error: Access to XMLHttpRequest at 'http://xxxx' from origin 'http://xxxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
原因:浏览器执行预检请求,后端接口返回302重定向,大部分浏览器将不继续进行请求,直接报错处理。出现该报错有以下几个原因:
-
接口对预检请求进行了登录判断,导致重定向到了登录页
-
后端接口未实现options预检请求,导致重定向到了404页面
解决:
-
排查是否支持预检请求,接口加上对options请求的支持
-
排查是否对预检请求进行了登录判断,去除掉登录判断
Error: Access to XMLHttpRequest at 'https://xxxxxx' from origin 'http://xxxx' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
原因:接口响应header中未返回Access-Control-Allow-Origin跨域允许头,导致浏览器对该请求直接block,就是我们俗称的不支持跨域请求。
解决:出现这种情况需要找运维在Nginx中给对应的域名统一加上Access-Control-Allow-Origin: * | 具体域。PS: 为什么由运维来处理?历史原因运维和接口方各自添加造成了混乱,导致接口出现了另一个*,*跨域报错,最终协商一致以后所有Access-Control-Allow-Origin都由运维来加,另外的几个跨域header由接口方加上。
Error: Access to XMLHttpRequest at 'https://presale.111.com.cn/disaster/guardSwitch/query' from origin 'http://localhost:8000' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.
原因:Access-Control-Allow-Origin响应header重复添加了导致报错,一般出现该原因是因为运维 和 接口方同时添加了Access-Control-Allow-Origin的响应header。
解决:Access-Control-Allow-Origin约定由运维统一添加,接口方应去掉代码中添加的Access-Control-Allow-Origin,如果代码中没有添加该头,但是也出现了该报错,需要排查一下是否是网关层添加导致,找@周晨敏 同学帮忙排查下
Error: Access to XMLHttpRequest at 'https://presale.111.com.cn/disaster/guardSwitch/query' from origin 'http://localhost:8000' has been blocked by CORS policy: Request header field xxxx is not allowed by Access-Control-Allow-Headers in preflight response.
原因:发起的跨域请求添加了自定义的header,浏览器会检查接口响应的access-control-allow-headers头,造成改报错可能是以下几个原因
-
未携带access-control-allow-headers响应头
-
携带的access-control-allow-headers响应的值中不包含 请求中自定义的header字段xxxx
-
2015年之前的浏览器不支持access-control-allow-headers: * 配置,必须返回具体字段
-
请求的content-type为application/json,返回的access-control-allow-headers中未包含content-type
解决:
-
检查响应头中是否已返回access-control-allow-headers,如未返回,请找接口方为接口添加access-control-allow-headers
-
检查接口的access-control-allow-headers值中是否已包含自定义请求中的header,一般access-control-allow-headers值是以xxxx, xxxx, xxxx, xxxx形式,比如自定义请求中添加了auth: xxxxx 字段,那么响应access-control-allow-headers必须包含 auth, xxxx, ...
-
如果请求的content-type为application/json,同样需要在access-control-allow-headers中包含content-type字段
Error: Access to XMLHttpRequest at 'https://xxxx' from origin 'http://xxxx' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
原因:如果跨域请求设置了withCredentials:true, 浏览器发起请求的时候会携带上所在域的cookie信息,同时跨域标准规定请求响应header中Access-Control-Allow-Origin不能设置*,必须设置为具体的跨域域名。
解决:如果业务需求确实需要跨域携带cookie信息,找运维同学将Access-Control-Allow-Origin改成具体的发起域
Error: Access to XMLHttpRequest at 'https://xxxx' from origin 'http://xxxx' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
原因:跨域请求设置了withCredentials: true,响应header中未包含Access-Control-Allow-Credentials跨域头,导致了报错。
解决:接口方在跨域请求的headers中添加Access-Control-Allow-Credentials: true 允许携带浏览器请求携带cookie信息。