浏览器的同源策略以及CORS和JSONP

什么是同源:

  所谓同源是指:域名、协议、端口相同

  域名:是否是一个域名下的资源

  协议:是否都属于http协议或者https协议

  端口:是否都无80 端口,或者其他端口

什么是浏览器的同源策略:

  同源策略是一种约定,它是浏览器最核心也是最基础的安全功能。它的核心在与它认为来自

  来自任何站点装载的信赖内容是不安全的,当被浏览器半信半疑的脚本运行在沙箱时,它们

  只被允许访问来自同一个站点的资源,而不是来自其他站点可能怀有恶意的资源。

  沙盒(sandbox),另称沙箱,是一种按照安全策略限制程序行为的执行环境。“沙盒”技术的实践运用流程是:让疑似病毒文件的可疑行为在虚拟的“沙盒”里充分运行,“沙盒”会记下它的每一个动作;当疑似病毒充分暴露了其病毒属性后,“沙盒”就会执行“回滚”机制:将病毒的痕迹和动作抹去,恢复系统到正常状态。

为什么要有跨域限制:

  XMLHttpRequest同源策略

  例如:AJAX同源策略主要是为了防止CSRF攻击,如果没有AJAX同源策略,

       HTTP请求都会带上请求地址对应的cookie。

  攻击流程:

       用户登录自己的银行页面myback.com,myback.com向用户的cookie中添加用户标识。

       用户浏览恶意页面evil.com,执行页面中ajax请求代码

          evil.com向myback.com发起ajax http请求,请求会默认把mybank.com对应的cookie也同时发送  

       银行页面从发送cookie中提取用户标识,验证无误,response返回数据到evil.com中,导致数据泄露

          由于ajax在后台执行,用户无法感知

  DOM同源策略:不同域之间的iframe不能相互访问

  假如没有了DOM同源策略,不同域之间的iframe可以互相访问

  攻击流程:1、做一个假网站,用iframe嵌套一个银行网站,mybank.com

          2、把iframe调整为和正真的银行网站没有区别

       3、如果用户在mybank.com中输入账号和密码,我们主网站可以跨域访问到mybank.com的dom节点,

          从而盗取账号密码

 

但是有时候我们需要突破域限制,去访问其他域下的资源

    1、电商网站想通过用户浏览器加载第三方快递网站的物流信息

    2、子站域名希望调用主站域名的用户资料接口,显示数据

    3、.....

如何解决跨域问题:

  CORS,全称跨域资源共享(Cross-Origin-Resourse-sharing),通过在HTTP请求中添加字段

  来告诉浏览器,哪些不同来源的服务器是可以访问本站资源,哪些不能访问。

  它允许浏览器向跨源服务器发出XHR请求,从而克服AJAX只能同源使用的限制。

  CORS需要浏览器和服务器同时支持。

  浏览器将CORS分为两类:简单请求和非简单请求

    区别:简单请求需要同时满足一下两大条件

        请求方法为:GET、HEAD、POST之一

        请求头信息不超过以下几个字段:Accept:

                       Accept-Language:

                       Content-Language:

                          Last-Event-ID:

                          Content-Type:只限于三个值,application/x-www-form-urlencoded

                               multipart/form-datatext/plain

        其他全部为非简单请求

 简单请求:

   基本流程:

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

    eg:如果浏览器发现这次属于跨源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字段,就知道出错了,会抛出一个错误,被XHR的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可以包含在请求中,一起发送给服务器,

        如果不需要包含cookie,删除该字段即可

    3、Access-Control-Expose-Headers

        该字段可选。CORS请求时,XHR对象的getRespouseHeader()方法只能拿到6个

       基本字段:Cache-Control、Content-Language、Content-Type

            Expires、Last-Modified、Pragma

       如果想拿到其他字段,需要在这里说明,例子中可以返回foobar字段的值。

  withCredentials属性:

  上面说到,CORS请求默认不发送cookie和http认证信息,

  如果要把cookie发送到服务器,一方面需要服务器同意

  即Access-Control-Allow-Credentials 需要为true

  另一方面,开发者需要在ajax请求中打开withCredentials

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

  否则,即使服务器接收,浏览器也不会发送请求

  如果省略withCredentials这个设置,有的浏览器还是会一起发送Cookie,这时,可以显示的关闭

 

xhr.withCredentials = false

  

  需要注意的是:如果要发送Cookie,Access-Control-Allow-Origin就不能设置为" * "号,

    必须指定明确的、与请求网页一致的域名。同时,cookie依然遵循同源策略,

    只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页

    代码中的document.cookie也无法读取服务器域名下的Cookie

  

 非简单请求:

  "预检"请求

  非简单请求是指对那种服务器有特殊要求的请求,

  比如请求方法PUT,DELETE或者Content-Type字段的类型为application/json

 

  非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)

  浏览器先询问服务器,当前网页所在的域名是否在服务器许可名单内,以及可以使用哪些HTTP动词和头字段。

  只有得到肯定的答复浏览器才会发出正式的XHR请求,否则会报错

  下面是一段js脚本

  

var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();

  代码中,HTTP请求的方法是OUT,并且发送一个自定义的消息头,X-Custom-Header。

  浏览器发现这是一个非简单请求之后就会主动发出"预检",要求头信息确认可以这样请求。预检头信息:

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

  "预检"的请求方法是OPTIONS,表示请求是用来询问的,关键字段Origin,表示来自哪个源,还有两个特殊字段

    Access-Control-Request-Method:表示浏览器的CORS请求会用到哪些http方法

    Access-Control-Request-Headers:表示浏览器的CORS请求会额外发送的头信息字段。

  

  预检请求的回应:

    服务器收到"预检"请求之后,

    检查了origin、Access-Control-Request-Method、Access-Control-Request-Headers字段以后,

    确认允许跨源请求,就可以做出回应

    

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://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

    http回应中,关键字段为Access-Control-Allow-Origin字段,表示某个url可以请求数据。

    该字段也可以设为" * ",表示同意任意跨源请求

    

    如果浏览器否定了" 预检 "请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,

    浏览器就会认定,服务器不允许跨源请求,因此触发一个错误,被XHR对象的onerror回调函数捕获,

    控制台打印如下信息。

    

XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.

    

    服务器回应的其他CORS字段

      Access-Control-Allow-Methods

        必须,是一个逗号分隔字符串,表示服务器支持的跨域请求方法

      Access-Control-Allow-Headers

        如果请求包含了Access-Control-Request-Headers,

        则此字段必须是一个逗号分隔的字符串,表示服务器所支持的所有头信息字段

      Access-Control-Allow-Credentials

        与请求时的含义相同

      Access-Control-Max-Age

        可选,用来指定本次预检的有效期,单位为秒。

  浏览器正常的请求和回应

    一旦服务器通过了"预检"请求,则以后每次浏览器正常的CORS请求,都跟简单请求一样,会有一个origin头字段信息。

    服务器也会回应一个 Access-Control-Allow-Origin 头字段信息。

    预检之后正常的CORS请求

      

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

    origin字段是浏览器自动添加的

    服务器正常回应

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

    上面头信息中,Access-Control-Allow-Origin字段是每次回应都必定包含的。

  JSONP跨域

    jsonp跨域基本原理:

      由于 script 标签不受浏览器同源策略的影响,允许跨域引用资源。

      因此可以通过动态创建script标签,然后利用 src 属性进行跨域。

    通过下面的例子来说明jsonp实现跨域的流程:

 1 // 1. 定义一个 回调函数 handleResponse 用来接收返回的数据
 2 function handleResponse(data) {
 3     console.log(data);
 4 };
 5 
 6 // 2. 动态创建一个 script 标签,并且告诉后端回调函数名叫 handleResponse
 7 var body = document.getElementsByTagName('body')[0];
 8 var script = document.gerElement('script');
 9 script.src = 'http://www.laixiangran.cn/json?callback=handleResponse';
10 body.appendChild(script);
11 
12 // 3. 通过 script.src 请求 `http://www.laixiangran.cn/json?callback=handleResponse`,
13 // 4. 后端能够识别这样的 URL 格式并处理该请求,然后返回 handleResponse({"name": "laixiangran"}) 给浏览器
14 // 5. 浏览器在接收到 handleResponse({"name": "laixiangran"}) 之后立即执行 ,也就是执行 handleResponse 方法,获得后端返回的数据,这样就完成一次跨域请求了。

    学习笔记,如有错误,请师傅们指出  

参考文章:

    https://juejin.im/post/5ba1d4fe6fb9a05ce873d4ad#heading-1

    http://www.ruanyifeng.com/blog/2016/04/cors.html

猜你喜欢

转载自www.cnblogs.com/s-qig57/p/12457477.html
今日推荐