跨域一

作者: 书中有凉气 | 来源:发表于2017-02-07 11:06 被阅读0次
要明白什么是跨域,先要了解什么是同源策略,它的含义是指:

A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源"。所谓"同源"指的是"三个相同"。

  • 协议相同
  • 域名相同
  • 端口相同

例如我的个人网站:http://www.tuituibang.top
协议是http://,域名是 www.tuituibang.top,端口是80(默认端口可以省略)
http://www.tuituibang.top/other.html:同源
http://example.com/dir/other.html:不同源(域名不同)
http://v2.www.tuituibang.top/other.html:不同源(域名不同)
http://www.tuituibang.top:8080/other.html:不同源(端口不同)
https://www.tuituibang.top/other.html:不同源(协议不同)

为什么要有同源策略,同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。如果是非同源的网站,则受到以下的限制:

  • Cookie、LocalStorage 和 IndexDB 无法读取。
  • DOM 无法获得。
  • AJAX 请求不能发送。
什么是跨域?跨域有几种实现形式
一:降域

例如:
one.tuituibang.com设置:document.domain = 'tuituibang.com'
two.tuituibang.com设置:document.domain = 'tuituibang.com'
one.tuituibang.comtwo.tuituibang.com就可以通过降域来带到跨域传递数据
注:
1.两个网页一级域名相同,只是二级域名不同,才能进行降域
2.这种方法只适用于 Cookie 和 iframe 窗口,LocalStorage 和 IndexDB 无法通过这种方法,规避同源政策
3.降域这个方法还是避免使用,有一定的安全隐患

二:window.postMessage

如果两个网页不同源,就无法拿到对方的DOM。典型的例子是iframe窗口和window.open方法打开的窗口,它们与父窗口无法通信。
HTML5为了解决这个问题,引入了一个全新的API:跨文档通信 API(Cross-document messaging)。
这个API为window对象新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。
举例:
父窗口http://aaa.com向子窗口http://bbb.com发消息,调用postMessage方法就可以了。

var popup = window.open('http://bbb.com/', 'title');
popup.postMessage('Hello World!', 'http://bbb.com/');

postMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为*,表示不限制域名,向所有窗口发送。

子窗口向父窗口发送消息的写法类似:

window.opener.postMessage('Nice to see you', 'http://aaa.com/');

父窗口和子窗口都可以通过message事件,监听对方的消息。

window.addEventListener('message', function(e) {
  console.log(e.data);
},false);

message事件的事件对象event,提供以下三个属性。

  • event.source:发送消息的窗口
  • event.origin: 消息发向的网址
  • event.data: 消息内容

下面的例子是,子窗口通过event.source属性引用父窗口,然后发送消息。

window.addEventListener('message', receiveMessage);
function receiveMessage(event) {
   event.source.postMessage('Nice to see you!', '*');
}

event.origin属性可以过滤不是发给本窗口的消息。

window.addEventListener('message', receiveMessage);
function receiveMessage(event) { 
  if (event.origin !== 'http://aaa.com/') return; 
  if (event.data === 'Hello World') { 
      event.source.postMessage('Hello', event.origin); 
  } 
  else { 
      console.log(event.data); 
  }
}
三:JSONP

JSONP是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,需要被跨域的网站服务端进行配合
例如:

function addScriptTag(src) { 
        var script = document.createElement('script');
        script.setAttribute("type","text/javascript"); 
        script.src = src; 
        document.body.appendChild(script);
}
window.onload = function () { 
        addScriptTag('http://example.com/ip?callback=foo');
}
function foo(data) { 
        console.log('Your public IP address is: ' + data.ip);
};

动态添加<script>标签,向服务器example.com发出请求。注意,该请求的查询字符串有一个callback参数,用来指定回调函数的名字,这对于JSONP是必需的。

后台的代码:

foo({
  "ip": "8.8.8.8"
});

由于<script>元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了foo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse的步骤。

JSONP的优点是:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都 可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。

JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。

四:CORS

CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。相比JSONP只能发GET请求,CORS允许任何类型的请求。

浏览器将CORS请求分成两类:######

简单请求(simple request)和非简单请求(not-so-simple request)。
简单请求:
(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

凡是不同时满足上面两个条件,就属于非简单请求。

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

GET /cors HTTP/1.1
Origin: http://api.bob.com/
Host: api.alice.com
Accept-Language: en-USConnection: 
keep-aliveUser-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字段的值。

要使用CORS,我们需要了解前端和服务器端的使用方法。
1、 前端
以前我们使用Ajax,代码类似于如下的方式:

var xhr = new XMLHttpRequest();  
xhr.open("GET", "/index.html", true);  
xhr.send();  

这里的“/index.html”是本域的相对路径。
如果我们要使用CORS,相关Ajax代码可能如下所示:

var xhr = new XMLHttpRequest();  
xhr.open("GET", "http://www.tuituibang.top/index.html", true);  
xhr.send();  

请注意:
代码与之前的区别就在于相对路径换成了其他域的绝对路径,也就是你要跨域访问的接口地址。
提供浏览器回退功能检测和支持,避免浏览器不支持的情况。

function createCORSRequest(method, url) {  
  var xhr = new XMLHttpRequest();  
  if ("withCredentials" in xhr) {  
    // 此时即支持CORS的情况  
    // 检查XMLHttpRequest对象是否有“withCredentials”属性  
    // “withCredentials”仅存在于XMLHTTPRequest2对象里  
    xhr.open(method, url, true);  
   
  } else if (typeof!= "undefined") {  
    // 否则检查是否支持XDomainRequest,IE8和IE9支持  
    // XDomainRequest仅存在于IE中,是IE用于支持CORS请求的方式  
    xhr = new XDomainRequest();  
    xhr.open(method, url);  
   
  } else {  
    // 否则,浏览器不支持CORS  
    xhr = null;    
  }  
  return xhr;  
}  
   
var xhr = createCORSRequest('GET', url);  
if (!xhr) {  
  throw new Error('CORS not supported');  
}  

服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。
Apache:Apache需要使用mod_headers模块来激活HTTP头的设置,它默认是激活的。你只需要在Apache配置文件的<Directory>, <Location>, <Files>或<VirtualHost>的配置里加入以下内容即可:

Header set Access-Control-Allow-Origin *  

后台PHP:

Access-Control-Allow-Origin: http://www.tuituibang.top

下面的设置使得只有http://www.tuituibang.top这个域才能跨域访问服务器的API。

目前只涉及到简单请求,非简单请求后续补充

相关文章

  • 跨域问题:好几种解决方案

    跨域分为广义跨域和狭义跨域 广义跨域:一个域下的文档或脚本试图去请求另一个域下的资源; 广义跨域可以分为以下几种:...

  • ajax readystatus=0;status=0 报错

    跨域 跨域 跨域 一定要找运维或者后台解决

  • 跨域问题总结

    跨域, 为什么需要跨域?跨域有什么不好?怎么实现跨域? 一、什么是跨域 只要协议、域名、端口有任何一个不同,都被当...

  • 跨域问题总结

    跨域, 为什么需要跨域?跨域有什么不好?怎么实现跨域? 一、什么是跨域 只要协议、域名、端口有任何一个不同,都被当...

  • 深入跨域问题(3) - 利用 JSONP 解决跨域

    深入跨域问题(1) - 初识 CORS 跨域资源共享;深入跨域问题(2) - 利用 CORS 解决跨域深入跨域问题...

  • 浏览器跨域的那些事

    整理中 目标: 了解跨域 解决跨域 服务器配置跨域(java, nginx) 前端调试时配置解决跨域 一、什么是跨...

  • SSM框架配置CORS跨域

    什么是跨域? 跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。 常见的跨域场景: 跨域资...

  • 关于设置env等环境变量的思考

    1、如何处理跨域后台处理跨域前端处理跨域浏览器处理跨域 前端本地处理跨域:代理线上跨域的处理方式:Nginx反向代...

  • Web前后端跨域问题处理

    跨域问题有前台跨域(iframe间)和后台跨域。 前台跨域的解决方案可以采用跨域文档通讯(Cross domain...

  • 跨域

    跨域 什么是跨域: 解决跨域 通过jsonp原理:在页面引入跨域js和css时,没有存在跨域问题.因此可以动态创建...

网友评论

      本文标题:跨域一

      本文链接:https://www.haomeiwen.com/subject/dpaeittx.html