前端跨域

一、同源策略

同源策略限制从一个源的资源如何与另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键机制。

二、怎样算同源

两个页面的协议、域名和端口完全相同,则两个页面是同源的。

三、同源限制范围

(1)Cookie , LocalStorage, IndexedDB 数据读取限制

(2)DOM无法获取。如iframe 窗口和window.open方法打开的窗口,无法与父窗口通信

(3)Ajax请求发送限制

四、什么是跨域

协议、域名、端口 有一个不同,就是不同域。对于协议和端口不同的跨域,只能通过后台解决。

 五、常用跨域的解决方案

(一)、跨域资源共享(CORS)——解决Ajax同源请求限制

概述:CORS允许Web应用服务器进行跨域访问,浏览器支持在API容器中(如XHR或Fetch)中使用CORS。 CORS需要浏览器与服务器的同时支持。目前IE10以上的浏览器都支持了该功能。因此,实现CORS的关键是服务器,必须要服务端实现了CORS接口,才实现跨域通信。浏览器端一旦发现Ajax请求跨域,会自动增加附加头信息。CORS新增了一组http头部字段,允许服务器声明哪些源站有权限访问哪些资源。对可能产生副作用的http请求,浏览器必须首先使用OPTIONS方法发起一个预检请求,在得到服务器允许确认允许后,才发起实际的http请求。

应用场景:——————简单请求、预检请求、带身份凭证(Cookie)的请求,以XHR为例,fetch同理,但按fetch标准略有不同

1、简单请求 

//request head
GET /resources/public-data/ HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre 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 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Connection: keep-alive Referer: http://foo.example/examples/access-control/simpleXSInvocation.html Origin: http://foo.example //response HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 00:23:53 GMT Server: Apache/2.0.61 Access-Control-Allow-Origin: * //*表示可以被任何域访问。如果仅仅允许http://foo.example 则将*替换为该地址即可 Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: application/xml

 使用Origin 和Access-Control-Allow-Origin 就能完成最简单的访问控制。

2、预检请求

 当请求满足下列任一条件时,应该首先发送预检请求:

  • 使用了下面任意http方法: put  delect  connect  options  trace patch
  • 人为设置了CORS安全的首部字段集合之外的其他首部字段。即:

 需要预检的http请求示例:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
    
function callOtherDomain(){
  if(invocation)
    {
      invocation.open('POST', url, true);
      invocation.setRequestHeader('X-PINGOTHER', 'pingpong');                     //需要预检的http请求
      invocation.setRequestHeader('Content-Type', 'application/xml');             //需要预检的http请求  
      invocation.onreadystatechange = handler;
      invocation.send(body); 
    }
}

 预检请求

//request head
OPTIONS /resources/post-here/ HTTP/1.1 //使用options方法的预检请求 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre 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 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Connection: keep-alive Origin: http://foo.example Access-Control-Request-Method: POST //告知服务器,实际请求将是POST方法 Access-Control-Request-Headers: X-PINGOTHER, Content-Type //告知服务器,请求将携带两个自定义首部字段X-PINGOTHER 和Content-Type。 服务器据此决定该请求是否被允许
//response
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 //允许的域为http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS //允许使用POST GET OPTIONS发送请求
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type //允许头部字段中携带 X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400 //改响应的有效时间为86400s。在有效期内浏览器无需为同一请求发起预检请求
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

 预检请求通过后发送实际请求

//request head
POST /resources/post-here/ HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre 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 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Connection: keep-alive X-PINGOTHER: pingpong Content-Type: text/xml; charset=UTF-8 Referer: http://foo.example/examples/preflightInvocation.html Content-Length: 55 Origin: http://foo.example Pragma: no-cache Cache-Control: no-cache <?xml version="1.0"?><person><name>Arun</name></person> //response HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:15:40 GMT Server: Apache/2.0.61 (Unix) Access-Control-Allow-Origin: http://foo.example Vary: Accept-Encoding, Origin Content-Encoding: gzip Content-Length: 235 Keep-Alive: timeout=2, max=99 Connection: Keep-Alive Content-Type: text/plain [Some GZIP'd payload]

 

3、带身份凭证(Cookie)的请求

 对于跨域的XHR和Fetch请求,浏览器不会发送身份凭证信息,如需发送,需要设置XHR的特殊标记withCredentials

带Cookie的跨域请求示例

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/';
    
function callOtherDomain(){
  if(invocation) {
    invocation.open('GET', url, true);
    invocation.withCredentials = true;         //携带cookie发送请求,服务端需要携带Access-Control-Allow-Credentials:ture, 否则浏览器不会把响应给请求发送者
    invocation.onreadystatechange = handler;
    invocation.send(); 
  }
}

 

携带Cookie的请求响应

//request head
GET /resources/access-control-with-credentials/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
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
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/credential.html
Origin: http://foo.example
Cookie: pageAccess=2

//response
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:34:52 GMT
Server: Apache/2.0.61 (Unix) PHP/4.4.7 mod_ssl/2.0.61 OpenSSL/0.9.7e mod_fastcgi/2.4.2 DAV/2 SVN/1.4.2
X-Powered-By: PHP/5.2.6
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 106
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

 

注:带身份凭证的请求Access-Control-Allow-Origin 值不可设置为“*”,需要指明具体值,否则请求会失败。

(二)、JSONP

JSONP由两部分组成:

  • 回调函数        当响应来到时页面中该调用的函数,回调函数名在请求中指定
  • 数据     传入回调函数中的JSON数据

JSONP跨域步骤:

  1、浏览器构造请求地址  

标准的script请求地址为: 请求的资源地址 + 获取函数的字段名 + 回调函数名。 获取函数的字段名需要客户端服务端共同约定,如jquery中默认为callback。

function resolveJson(result) {
    console.log(result.name);
}
var jsonpScript= document.createElement("script");
jsonpScript.type = "text/javascript";
jsonpScript.src = "http://www.qiute.com?callbackName=resolveJson";     //callbackName 获取函数的字段名  resolveJson回调函数
document.getElementsByTagName("head")[0].appendChild(jsonpScript);

 

  2、服务端构造返回

在收到浏览器的请求后,从url中按约定的获取函数字段寻找回调函数名,即按照callbackName找到了回调函数为resolveJson,然后将数据传入回调函数

resolveJson({name: 'qiutc'});

 

  3、客户端以脚本方式执行客户端返回值    客户端拿到回调数据后,自行处理

  

参考:

JavaScript高级程序设计

https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy 浏览器的同源策略

http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html 浏览器的同源策略及其规避方法

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS http访问控制

https://segmentfault.com/a/1190000006095018   浏览器跨域方法与基于Fetch的Web请求最佳实践

https://segmentfault.com/a/1190000000718840  详解js跨域问题

https://github.com/camsong/blog/issues/2   fetch简介

 

posted on 2018-01-16 14:26  小小驰  阅读(855)  评论(0编辑  收藏  举报