赞
踩
相信前端开发必定少不了这个问题。
跨域是浏览器的行为,跨域问题其实就是浏览器的同源策略所导致的。同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。
当跨域的时候会报如下错误:
以下协议、域名、端口一致。
http://www.example.com:80/a.js
http://www.example.com:80/b.js
以下这种看上去再相似也没有用,都不是同源。
http://www.example.com:8080
http://www2.example.com:80
注意:
在这里注意一下啊,这里是为了突出端口的区别才写上端口。在默认情况下 http 可以省略端口 80, https 省略 443。这别到时候闹笑话了,你和我说http://www.example.com:80和http://www.example.com不是同源,他俩是一个东西。
http://www.example.com:80\===http://www.example.com
https://www.example.com:443\===https://www.example.com
1.JSONP
JSONP主要就是利用了script标签没有跨域限制的这个特性来完成的,主要是利用src这个属性来发起get请求,并且跨可以接收回调。
使用限制
仅支持 GET 方法,如果想使用完整的 REST 接口,请使用 CORS 或者其他代理方式。
流程解析
1.前端定义解析函数(例如 jsonpCallback=function(){…})
2.通过 params 形式包装请求参数,并且声明执行函数(例如 cb=jsonpCallback)
3.后端获取前端声明的执行函数(jsonpCallback),并以带上参数并调用执行函数的方式传递给前端。
2.CORS
跨域资源共享(CORS) 是一种机制,它使用额外的HTTP头来告诉浏览器 让运行在一个 origin (domain) 上的 Web 应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。
通常在我们项目这种方式会用的比较多,主要是前后端配合来配置:
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
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: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
而在 cors 中会有简单请求和复杂请求的概念。
简单请求:
如下是一个需要执行预检请求的 HTTP 请求:
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://bar.other/resources/post-here/');
xhr.setRequestHeader('X-PINGOTHER', 'pingpong');
xhr.setRequestHeader('Content-Type', 'application/xml');
xhr.onreadystatechange = handler;
xhr.send('<person><name>Arun</name></person>');
备注: 如下所述,实际的 POST 请求不会携带 Access-Control-Request-* 首部,它们仅用于 OPTIONS 请求。
下面是服务端和客户端完整的信息交互。首次交互是 预检请求/响应:
OPTIONS /doc HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
HTTP/1.1 204 No Content
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2
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
Vary: Accept-Encoding, Origin
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
OPTIONS 是 HTTP/1.1 协议中定义的方法,用以从服务器获取更多信息。该方法不会对服务器资源产生影响。 预检请求中同时携带了下面两个首部字段:
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
首部字段 Access-Control-Request-Method 告知服务器,实际请求将使用 POST 方法。首部字段 Access-Control-Request-Headers 告知服务器,实际请求将携带两个自定义请求首部字段:X-PINGOTHER 与 Content-Type。服务器据此决定,该实际请求是否被允许。
服务器响应内容如下:
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
服务器的响应携带了 Access-Control-Allow-Origin: https://foo.example,从而限制请求的源域。同时,携带的 Access-Control-Allow-Methods 表明服务器允许客户端使用 POST 和 GET 方法发起请求(与 Allow 响应首部类似,但其具有严格的访问控制)。
首部字段 Access-Control-Allow-Headers 表明服务器允许请求中携带字段 X-PINGOTHER 与 Content-Type。与 Access-Control-Allow-Methods 一样,Access-Control-Allow-Headers 的值为逗号分割的列表。
最后,首部字段 Access-Control-Max-Age 表明该响应的有效时间为 86400 秒,也就是 24 小时。在有效时间内,浏览器无须为同一请求再次发起预检请求。请注意,浏览器自身维护了一个 最大有效时间,如果该首部字段的值超过了最大有效时间,将不会生效。
预检请求完成之后,发送实际请求
3.Node 正向代理
代理的思路为,利用服务端请求不会跨域的特性,让接口和当前站点同域。
a.cli 工具中的代理
// Webpack (4.x)
devServer: {
port: 8000,
proxy: {
"/api": {
target: "http://localhost:8080"
}
}
// config/index.js
// Vue-cli 2.x
...
proxyTable: {
'/api': {
target: 'http://localhost:8080',
}
},
// Vue-cli 3.x
module.exports = {
devServer: {
port: 8000,
proxy: {
"/api": {
target: "http://localhost:8080"
}
}
}
};
4.Nginx 反向代理
// 配置 nginx
server {
listen 80;
server_name local.test;
location /api {
proxy_pass http://localhost:8080;
}
location / {
proxy_pass http://localhost:8000;
}
}
5.Websocket
这种方式本质没有使用了 HTTP 的响应头, 因此也没有跨域的限制。
6.window.postMessage
window.postMessage()方法可以安全地实现跨源通信。通常,对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为 https),端口号(443 为 https 的默认值),以及主机 (两个页面的模数Document.domain设置为相同的值) 时,这两个脚本才能相互通信。window.postMessage()方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。
用途
1.页面和其打开的新窗口的数据传递
2.多窗口之间消息传递
3.页面与嵌套的 iframe 消息传递
7.浏览器开启跨域(终极方案)
其实讲下其实跨域问题是浏览器策略,源头是他,那么能否能关闭这个功能呢?
答案是肯定的。
注意事项: 因为浏览器是众多 web 页面入口。我们是否也可以像客户端那种,就是用一个单独的专门宿主浏览器,来打开调试我们的开发页面。例如这里以 chrome canary 为例,这个是我专门调试页面的浏览器,不会用它来访问其他 web url。因此它也相对于安全一些。当然这个方式,只限于当你真的被跨域折磨地崩溃的时候才建议使用以下。使用后,请以正常的方式将他打开,以免你不小心用这个模式干了其他的事。
在最一开始,我们知道了,跨域只存在于浏览器端。而浏览器为 web 提供访问入口。我们在可以浏览器内打开很多页面。正是这样的开放形态,所以我们需要对他有所限制。就比如林子大了,什么鸟都有,我们需要有一个统一的规范来进行约定才能保障这个安全性。
参考资料:
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS#%E5%8A%9F%E8%83%BD%E6%A6%82%E8%BF%B0
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。