深度解析跨域

深度解析跨域

什么叫跨域?

跨域,指的是浏览器不能执行其余网站的脚本。 它是由浏览器的同源策略形成的,是浏览器施加的安全限制。 所谓同源策略是指,协议、端口、域名都相同。javascript

形成跨域的两种策略

浏览器的同源策略会致使跨域,这里同源策略又分为如下两种:html

  • DOM同源策略:禁止对不一样源页面DOM进行操做。这里主要场景是iframe跨域的状况,不一样域名的iframe是限制互相访问的。
  • XmlHttpRequest同源策略:禁止使用XHR对象向不一样源的服务器地址发起HTTP请求。

为何要有跨域限制

浏览器执行脚本时,会检查脚本是否同源,若是不是,则不会被执行。java

两个域名不能跨过域名来发送数据和请求数据,不然就是不安全的,这种不安全也就是CSRF(跨站请求伪造)攻击。jquery

若是没有AJAX同源策略,至关危险,咱们发起的每一次HTTP请求都会带上请求地址对应的cookie,CSRF 攻击是利用用户的登陆态发起恶意请求。web

如何解决跨域

  1. JSONP
  2. CORS
  3. 服务器代理
  4. document.domain跨子域
  5. location.hash跨域
  6. postMessage

JSONP

JSONP的产生:ajax

  1. AJAX直接请求普通文件存在跨域无权限访问的问题,不论是静态页面也好.json

  2. 不过咱们在调用js文件的时候又不受跨域影响,好比引入jquery框架的,或者是调用相片的时候api

  3. 凡是拥有src这个属性的标签均可以跨域例如<script> <img> <iframe>跨域

  4. 若是想经过纯web端跨域访问数据只有一种可能,那就是把远程服务器上的数据装进js格式的文件里.浏览器

  5. 而json又是一个轻量级的数据格式,还被js原生支持

  6. 为了便于客户端使用数据,逐渐造成了一种非正式传输协议,人们把它称做JSONP,该协议的一个要点就是容许用户传递一个callback 参数给服务端。

基于script跨域

<script src="http://domain/api?param1=a&param2=b&callback=jsonp"></script>
<script>
    function jsonp(data) {
    	console.log(data)
	}
</script> 
复制代码

在开发中可能会遇到多个 JSONP 请求的回调函数名是相同的,这时候就须要本身封装一个 JSONP,如下是简单实现:

function jsonp(url, jsonpCallback, success) {
  let script = document.createElement('script')
  script.src = url
  script.async = true
  script.type = 'text/javascript'
  window[jsonpCallback] = function(data) {
    success && success(data)
  }
  document.body.appendChild(script)
}
jsonp('http://xxx', 'callback', function(value) {
  console.log(value)
})
复制代码

基于jQuery跨域

$.ajax({
    url:'http://domain/api',
    ype : 'get',
    dataType : 'text',
    success:function(data){
        alert(data);
    },
    error:function(data){
        alert(2);
    }        
});
复制代码

经过iframe来跨子域

eg: a.study.cn/a.html 请求 b.study.cn/b.html

a.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
        <script type="text/javascript">
            document.domain = 'study.cn';
            function test() {
                alert(document.getElementById('a').contentWindow);
            }
        </script>
</head>
<body>
    <iframe id='a' src='http://b.study.cn/b.html' onload='test()'>
</body>
</html>
复制代码

b.html

<!DOCTYPE html>
<html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
        <script type="text/javascript">
        document.domain = 'study.cn';
        </script>
    </head>
    <body>
        我是b.study.cn的body
    </body>
</html>
复制代码

修改document.domain的方法只适用于不一样子域的框架(父类与子类)间的交互。

CORS

支持CORS请求的浏览器一旦发现ajax请求跨域,会对请求作一些特殊处理,对于已经实现CORS接口的服务端,接受请求,并作出回应。

有一种状况比较特殊,若是咱们发送的跨域请求为“非简单请求”,浏览器会在发出此请求以前首先发送一个请求类型为OPTIONS的“预检请求”,验证请求源是否为服务端容许源,这些对于开发这来讲是感受不到的,由浏览器代理。

总而言之,客户端不须要对跨域请求作任何特殊处理。

浏览器对跨域请求区分为“简单请求”与“非简单请求”

简单请求

(1) 请求方法是如下三种方法之一:

HEAD

 GET

 POST
复制代码

(2)HTTP的头信息不超出如下几种字段:

Accept

 Accept-Language

 Content-Language

 Last-Event-ID

 Content-Type:
     application/x-www-form-urlencoded、 multipart/form-data、text/plain
复制代码

不知足这些特征的请求称为“非简单请求”,例如:content-type=applicaiton/json , method = PUT/DELETE...

浏览器判断跨域为简单请求时候,会在Request Header中添加 Origin (协议 + 域名 + 端口)字段 , 它表示咱们的请求源,CORS服务端会将该字段做为跨源标志。

CORS接收到这次请求后 , 首先会判断Origin是否在容许源(由服务端决定)范围以内,若是验证经过,服务端会在Response Header 添加 Access-Control-Allow-Origin、Access-Control-Allow-Credentials等字段。

必须字段:
Access-Control-Allow-Origin:表示服务端容许的请求源,*标识任何外域,多个源 , 分隔

可选字段
Access-Control-Allow-Credentials:false 表示是否容许发送Cookie,设置为true
                                 同时,ajax请求设置withCredentials = true,浏览
                                 器的cookie就能发送到服务端

Access-Control-Expose-Headers:调用getResponseHeader()方法时候,能从header中获
                               取的参数
复制代码

总结:简单请求只须要CORS服务端在接受到携带Origin字段的跨域请求后,在response header中添加Access-Control-Allow-Origin等字段给浏览器作同源判断

进行非简单请求时候, 浏览器会首先发出类型为OPTIONS的“预检请求”,请求地址相同 , CORS服务端对“预检请求”处理,并对Response Header添加验证字段,客户端接受到预检请求的返回值进行一次请求预判断,验证经过后,主请求发起。

例如:发起 content-type=application/json 的非简单请求,这时候传参要注意为json字符串

这里能够看到,浏览器连续发送了两个jsonp.do请求 , 第一个就是“预检请求”,类型为OPTIONS,由于咱们设置了content-type这个属性,因此预检请求的Access-Control-Expose-Headers必须携带content-type,不然预检会失败。

预检经过后,主请求与简单请求一致。

总结:非简单请求须要CORS服务端对OPTIONS类型的请求作处理,其余与简单请求一致

经过上面叙述,咱们得知借助CORS咱们没必要关心发出的请求是否跨域,浏览器会帮咱们处理这些事情,可是服务端须要支持CORS,服务端实现CORS的原理也很简单,在服务端彻底能够对请求作上下文处理,已达到接口容许跨域访问的目的。

服务器代理

浏览器有跨域限制,可是服务器不存在跨域问题,因此能够由服务器请求所要域的资源再返回给客户端。

document.domain

该方式只能用于二级域名相同的状况下,好比 a.test.com 和 b.test.com 适用于该方式。

只须要给页面添加 document.domain = 'test.com' 表示二级域名都相同就能够实现跨域。

location.hash跨域

location.hash方式跨域,是子框架具备修改父框架src的hash值,经过这个属性进行传递数据,且更改hash值,页面不会刷新。可是传递的数据的字节数是有限的。

postMessage

页面和新开的窗口的数据交互。

  • 多窗口之间的数据交互。
  • 页面与所嵌套的iframe之间的信息传递。
  • window.postMessage是一个HTML5的api,容许两个窗口之间进行跨域发送消息。

这个应该就是之后解决dom跨域通用方法了,具体能够参照MDN。

// 发送消息端
window.parent.postMessage('message', 'http://test.com')
// 接收消息端
var mc = new MessageChannel()
mc.addEventListener('message', event => {
  var origin = event.origin || event.originalEvent.origin
  if (origin === 'http://test.com') {
    console.log('验证经过')
  }
})
复制代码

参考:

跨域——CORS详解

跨域的那些事儿

相关文章
相关标签/搜索