前言
在接口联调时偶尔会遇到跨系统调用不同域名,被浏览器限制,导致生产无法掉通从而发生事故。那前端能怎么优化避免这类问题呢?
为什么会跨域
因为浏览器的同源策略的原因,被限制发出请求以保证安全。
同源策略是一个重要的安全策略,它用于限制一个同源的文档或者它加载的脚本与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。
同源的定义
域名、协议和端口的不同, 都判定为不同源
下表给出了与URL http://store.company.com/dir/page.html 的源进行对比的示例:
| URL | 结果 | 原因 |
|---|---|---|
http://store.company.com/dir2/other.html | 同源 | 只有路径不同 |
http://store.company.com/dir/inner/another.html | 同源 | 只有路径不同 |
https://store.company.com/secure.html | 失败 | 协议不同 |
http://store.company.com:81/dir/etc.html | 失败 | 端口不同 ( http:// 默认端口是 80) |
http://news.company.com/dir/other.html | 失败 | 主机/域名不同 |
如何允许跨源访问
可以使用CORS来允许跨源访问。CORS 是 HTTP 的一部分,它允许服务端来指定哪些主机可以从这个服务端加载资源。
跨源资源共享(cors)是一种基于http头的机制, 该机制通过允许服务器其它源(域、协议和端口)加载自己的资源。以降低跨源 HTTP 请求所带来的风险。
应用cors
在浏览器中
1.跨源资源共享标准新增了一组HTTP 标头字段,设置对应的http头表示允许服务器声明哪些源站,通过浏览器有权限访问哪些资源
2.对服务器数据产生副作用的复杂 HTTP 请求方法(特别是 GET以外的 HTTP 请求,或者搭配某些 MIME 类型 的 POST 请求)
。
浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨源请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(例如Cookie 和 HTTP 认证 相关数据)。
http头的设置(跨域需携带HTTP头)
- 响应头:
- Access-Control-Allow-Origin
携带凭证时必须携带凭证时要指定域名,不能使用通配符*否则请求将会失败 - Access-Control-Allow-Credentials
携带凭证时必须携带凭证时设置为 true 时是允许浏览器跨域读取 response 的内容
- 请求头:
- Origin
必须请求自动携带,无需手动设置 - Access-Control-Request-Method
非必须用于预检请求。其作用是,将实际请求所使用的 HTTP 方法告诉服务器。与服务端[Access-Control-Allow-Methods]对应 - Access-Control-Request-Headers
非必须用于预检请求。其作用是,将实际请求所携带的请求头告诉服务器。服务器端响应头 [Access-Control-Allow-Headers] 进行回应
注意事项
- 使用了
自定义的请求头则不是简单请求,请求会发起“预检请求”,如果服务端响应头Access-Control-Allow-Headers未允许这些自定义头,将请求失败!
所以使用简单请求规定外的请求头, 需要与后端同事商定处理!否则酿成大错
- 对于跨域请求,浏览器不会发送身份凭证信息(登录态的Cookie)。如果要发送,需要设置
XMLHttpRequest对象的特殊标志位[withCredentials= true]。服务器需要在响应头携带Access-Control-Allow-Credentials: true,否则浏览器将不会把响应内容返回给请求的发送者。
例子:
const invocation = new XMLHttpRequest();
const url = "https://bar.other/resources/credentialed-content/";
function callOtherDomain() {
if (invocation) {
invocation.open("GET", url, true);
invocation.withCredentials = true;
invocation.onreadystatechange = handler;
invocation.send();
}
}
- 服务器不能将
Access-Control-Allow-Methods的值设为通配符“*”,而应将其设置为特定请求方法名称的列表,如:Access-Control-Allow-Methods: POST, GET