js跨域那些事儿

解决跨域,首先你得了解一个概念——同源策略

同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。只要协议、域名、端口有任何一个不同,都被当作是不同的域。

那要怎样读取资源呢?这就聊到了我们今天的主题——js跨域。

跨域解决方案

1、 通过jsonp跨域
2、 document.domain + iframe跨域
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域资源共享(CORS)
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域

这里我说几种:

1、CORS

基本思想:使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或相应是否成功,还是失败。

CORS原理:只需要向响应头header中注入Access-Control-Allow-Origin,这样浏览器检测到header中的Access-Control-Allow-Origin,则就可以跨域操作了。如果没有这个头部或者有这个头部但源信息不匹配,浏览器就会驳回请求。

基本流程:

对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。

下面是一个例子,浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin字段。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面的头信息中,Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

上面的头信息之中,有三个与CORS请求相关的字段,都以Access-Control-开头。

(1)Access-Control-Allow-Origin

该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。

(2)Access-Control-Allow-Credentials

该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。

(3)Access-Control-Expose-Headers

该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader(‘FooBar’)可以返回FooBar字段的值。

需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。

优点

CORS 通信与同源的 AJAX 通信没有差别,代码完全一样,容易维护。
支持所有类型的 HTTP 请求。

缺点

存在兼容性问题,特别是 IE10 以下的浏览器。
第一次发送非简单请求时会多一次请求。

2、JSONP

JSONP:是JSON with padding(填充式JSON或参数式JSON)的简写,它由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数,回调函数的名字一般是在请求中指定的,而数据就是传入回调函数中的JSON数据。

原理:如果一个页面加载了一个外来的JS文件,浏览器就会自动执行这个文件中的代码。

JSONP是通过动态

JSONP之所以在开发人员中极为流行,主要是原因是它非常简单易用。与图像Ping相比,它的优点在于能够直接访问相应文本,支持在浏览器与服务器之间双向通信。

缺点:

1.JSONP是从其他域中加载代码执行。如果其他域不安全,则可能会夹杂恶意代码。

2.要确定JSONP请求失败并不容易。

3.只能使用get方式进行提交,不适合数据量较大请求。

这种方式这两年用的不是很多。

3、document.domain+iframe

对于主域相同而子域不同的例子,可以通过设置document.domain的办法来解决。

举www.a.com/a.html和a.com/b.html为例,只需在a.html中添加一个b.html的iframe,并且设置两个页面的document.domain都为’a.com’(只能为主域名),两个页面之间即可互相访问了,代码如下:

www.a.com/a.html中的script

document.domain='a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://a.com/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
  //获取iframe的document对象
  //W3C的标准方法是iframe.contentDocument,
  //IE6、7可以使用document.frames[ID].document
  //为了更好兼容,可先获取iframe的window对象iframe.contentWindow
  var doc = ifr.contentDocument || ifr.contentWindow.document;
  // 在这里操纵b.html
  alert(doc.getElementById("test").innerHTML);
};

a.com/b.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title></title>
<script type="text/javascript">
  document.domain='a.com';
</script>
</head>
<body>
<h1 id="test">Hello World</h1>
</body>
</html>

如果b.html要访问a.html,可在子窗口(iframe)中通过window.parent来访问父窗口的window对象,然后就可以为所欲为了(window对象都有了,还有啥不行的),同理子窗口也可以和子窗口之间通信。

于是,我们可以通过b.html的XMLHttpRequest来获取数据,再传给a.html,从而解决跨子域获取数据的问题。

但是这种方法只支持同一根域名下的页面,如果不同根域名(例如baidu.com想访问google.com)那就无能为力了。

参考来自:
https://blog.csdn.net/jiangzh1982/article/details/84055725
https://blog.csdn.net/w3624270/article/details/81484243

发布了8 篇原创文章 · 获赞 0 · 访问量 1906

猜你喜欢

转载自blog.csdn.net/weixin_44296489/article/details/88697767