跨域是什么

  跨域就是指从一个域名的网页去请求另一个域名的资源,因为JavaScript同源策略的限制,资源无法获取。比如从www.baidu.com 页面去请求 www.google.com 的资源,这是就要用到跨域请求了。严格一点来说就是:只要 协议,域名,端口有任何一个的不同,就被当作是跨域。详情如下:

特别注意两点:

  第一,如果是协议和端口造成的跨域问题“前台”是无能为力的,

  第二:在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。

浏览器为什么要限制跨域访问

  原因当然就是安全问题:如果没有限制跨域访问,那么一个页面就可以随意地访问另外一个网站的资源,那么就有可能在客户完全不知情的情况下出现安全问题。比如下面的操作就有安全问题:

  ·用户访问http://www.icbc.com.cn/icbc/ ,登陆并进行网银操作,这时一些个人信息就可以通过cookie等方式生成并存放在浏览器中

  ·假如此时用户去访问其他恶意网站

  ·这时该恶意网站就可以在它的页面中,拿到银行的cookie,比如用户名,登陆token等,然后发起对http://www.icbc.com.cn/icbc/ 的操作。

   · 如果这时浏览器不予限制,并且银行也没有做响应的安全处理的话,那么用户的信息有可能就这么泄露了。

那为什么还要使用跨域呢

  有时公司内部有多个不同的子域,比如一个是location.company.com ,而应用是放在app.company.com , 这时想从 app.company.com去访问 location.company.com 的资源就属于跨域。

跨域的几种方式

1、JSONP

  虽然浏览器默认禁止了跨域访问,但是跨域访问中有图片、css、javascript脚本文件等是不限制,因此你可以在页面渲染时动态在<script>标签设置src路径,而这个路径返回回来的就是json对象。根据这一点,可以方便地通过创建script节点的方法来实现完全跨域的通信,可以自由执行引入的JS文件中的function(包括操作cookie、Dom等等)。具体的做法可以参考YUI的Get Utility

jsonp是一种跨域通信的手段,它的原理其实很简单:

  1、首先是利用script标签的src属性来实现跨域。

  2、通过将前端方法作为参数传递到服务器端,然后由服务器端注入参数(我们需要的json数据)之后再返回,实现服务器端向客户端通信。

  3、由于使用script标签的src属性,因此只支持get方法

优点是兼容性好,简单易用,支持浏览器与服务器双向通信。缺点是只支持GET请求。

实现流程

  1、设定一个script标签

<script src="http://jsonp.js?callback=xxx"></script>

  2、callback定义了一个函数名,而远程服务端通过调用指定的函数并传入参数来实现传递参数,将fn(response)传递回客户端

$callback = !empty($_GET['callback']) ? $_GET['callback'] : 'callback';

echo $callback.'(.json_encode($data).)';

  3、客户端接收到返回的js脚本,开始解析和执行fn(response)

jsonp简单实现

  一个简单的jsonp实现,其实就是拼接url,然后将动态添加一个script元素到头部。

function jsonp(req){
var script = document.createElement('script');
var url = req.url + '?callback=' + req.callback.name;
script.src = url;
document.getElementsByTagName('head')[].appendChild(script);
}

  前端js示例

function hello(res){
alert('hello ' + res.data);
} jsonp({
url : '',
callback : hello
});

  服务器端代码

var http = require('http');
var urllib = require('url');
var port = ;
var data = {'data':'world'};
http.createServer(function(req,res){
var params = urllib.parse(req.url,true);
if(params.query.callback){
console.log(params.query.callback);
//jsonp
var str = params.query.callback + '(' + JSON.stringify(data) + ')';
res.end(str);
} else {
res.end();
}
}).listen(port,function(){
console.log('jsonp server is on');
});

  然而,这个实现虽然简单,但有一些不足的地方:

    1、我们传递的回调必须是一个全局方法,我们都知道要尽量减少全局的方法。

    2、需要加入一些参数校验,确保接口可以正常执行。

可靠的jsonp

(function (global) {
var id = ,
container = document.getElementsByTagName("head")[]; function jsonp(options) {
if(!options || !options.url) return; var scriptNode = document.createElement("script"),
data = options.data || {},
url = options.url,
callback = options.callback,
fnName = "jsonp" + id++; // 添加回调函数
data["callback"] = fnName; // 拼接url
var params = [];
for (var key in data) {
params.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));
}
url = url.indexOf("?") > ? (url + "&") : (url + "?");
url += params.join("&");
scriptNode.src = url; // 传递的是一个匿名的回调函数,要执行的话,暴露为一个全局方法
global[fnName] = function (ret) {
callback && callback(ret);
container.removeChild(scriptNode);
delete global[fnName];
} // 出错处理
scriptNode.onerror = function () {
callback && callback({error:"error"});
container.removeChild(scriptNode);
global[fnName] && delete global[fnName];
} scriptNode.type = "text/javascript";
container.appendChild(scriptNode)
} global.jsonp = jsonp; })(this);
使用示例 jsonp({
url : "www.example.com",
data : {id : },
callback : function (ret) {
console.log(ret);
}
});

2、CORS

  CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

  CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

  整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

  因此,实现CORS通信的关键是服务器。只要服务器实现了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
  凡是不同时满足上面两个条件,就属于非简单请求。
  浏览器对这两种请求的处理,是不一样的。

简单请求的实现

  对于简单请求,浏览器直接发出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-

  上面的头信息之中,有三个与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-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。

withCredentials 属性

  上面说到,CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。

Access-Control-Allow-Credentials: true

  另一方面,开发者必须在AJAX请求中打开withCredentials属性。

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

  否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。

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

xhr.withCredentials = false;

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

与JSONP比较

  CORS与JSONP的使用目的相同,但是比JSONP更强大。

  JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。

3、document.domain+iframe

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

  前提条件:这两个域名必须属于同一个基础域名!而且所用的协议,端口都要一致,否则无法利用document.domain进行跨域

  具体的做法是可以在http://www.a.com/a.html和http://script.a.com/b.html两个文件中分别加上document.domain = ‘a.com’;然后通过a.html文件中创建一个iframe,去控制iframe的contentDocument,这样两个js文件之间就可以“交互”了。当然这种办法只能解决主域相同而二级域名不同的情况,如果你异想天开的把script.a.com的domian设为alibaba.com那显然是会报错地!代码如下:

www.a.com上的a.html

document.domain = 'a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://script.a.com/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
var doc = ifr.contentDocument || ifr.contentWindow.document;
// 在这里操纵b.html
alert(doc.getElementsByTagName("h1")[].childNodes[].nodeValue);
};

script.a.com上的b.html

document.domain = 'a.com';

问题:

  1、安全性,当一个站点(b.a.com)被攻击后,另一个站点(c.a.com)会引起安全漏洞。

  2、如果一个页面中引入多个iframe,要想能够操作所有iframe,必须都得设置相同domain。

  还有一点要注意,这个方法需要在iframe加载后才能使用!

4、还有几种方式,暂且不说,未完待续。。。。。。

使用window.name来进行跨域

  window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的

使用HTML5中新引进的window.postMessage方法来跨域传送数据

  还有flash、在服务器上设置代理页面等跨域方式。个人认为window.name的方法既不复杂,也能兼容到几乎所有浏览器,这真是极好的一种跨域方法。

参考:

浅谈JSONP

说说JSON和JSONP,也许你会豁然开朗,含jQuery用例

jsonp的原理与实现

你所不知道的跨域资源共享(CORS)

跨域资源共享 CORS 详解

JavaScript中的跨域的更多相关文章

  1. 在javascript中的跨域解决

    跨域产生的原因 跨域是由浏览器的同源策略引起的,即不同源(协议,域名,端口中其中有一个不同)的js是不能读取对方的资源的.当要网站中的js要请求其他网站的数据时就会产生跨域问题,就像下面这样,浏览器会 ...

  2. JavaScript中的跨域详解(二)

    4.AJAX 同源政策规定,AJAX请求只能发给同源的网址,否则就报错. 除了架设服务器代理(浏览器请求同源服务器,再由后者请求外部服务),有三种方法规避这个限制. JSONP WebSocket C ...

  3. JavaScript中的跨域详解(一)

    同源策略 所谓的同源策略,指的是浏览器对不同源的脚本或者文本访问方式进行的限制. 所谓同源,就是指两个页面具有相同的协议,主机(也常说域名),端口,三个要素缺一不可. 同源政策的目的,是为了保证用户信 ...

  4. JavaScript中的跨域问题

    跨域问题其实很普遍的存在的,如何解决跨域问题呢,跨域问题到底是怎么产生的,解决方法的由来又是什么?我觉得看了视频讲解,值得写下来,记录下来. 一.跨域问题是怎么产生? 概念:只要协议.域名.端口有任何 ...

  5. js中各种跨域问题实战小结(二)

    这里接上篇:js中各种跨域问题实战小结(一) 后面继续学习的过程中,对上面第一篇有稍作休整.下面继续第二部分: -->5.利用iframe和location.hash -->6.windo ...

  6. js中各种跨域问题实战小结(一)

    什么是跨域?为什么要实现跨域呢? 这是因为JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象.也就是说只能访问同一个域中的资源.我觉得这就有必要了解下javascript中的同源策略 ...

  7. 在ASP.NET 5应用程序中的跨域请求功能详解

    在ASP.NET 5应用程序中的跨域请求功能详解 浏览器安全阻止了一个网页中向另外一个域提交请求,这个限制叫做同域策咯(same-origin policy),这组织了一个恶意网站从另外一个网站读取敏 ...

  8. javascript ajax 脚本跨域调用全解析

    javascript ajax 脚本跨域调用全解析 今天终于有点时间研究了一下javsscript ajax 脚本跨域调用的问题,先在网上随便搜了一下找到一些解决的办法,但是都比较复杂.由是转到jqu ...

  9. jQuery中getJSON跨域原理详解

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp28 jQuery中getJSON跨域原理详解 前几天我再开发一个叫 河蟹工 ...

随机推荐

  1. easyUI-combotree的本地数据导入

    一.页面内容: <div style="margin:10px 0"> <a href="javascript:void(0)" class= ...

  2. 对象Clone

    //================================================= // File Name : Clone_demo //-------------------- ...

  3. Unity Sprite Atlas Compression

    http://forum.unity3d.com/threads/2d-sprite-packer-and-pvrtc.218633/ http://docs.unity3d.com/Manual/S ...

  4. yum安装指定(特定)版本(旧版本)软件包的方法

    在命令行里输入: yum list SDL 注意这里类库的名字是区别大小写的. 参考 http://www.dabu.info/yum-install-specific-version-old-pac ...

  5. js随机生成N位数

    function RondomPass(number){ var arr = new Array; "); ;i<number;i++){ ); arr[i] =arr1[n] ; / ...

  6. MySQL-curses/termcap缺失

    环境:通前篇 1.错误:缺少 /curses/temrcap checking for termcap functions library... configure: error: No curses ...

  7. osharpV3数据库初始化

    var databaseInitializer = new DatabaseInitializer(); databaseInitializer.MapperAssemblyFinder = new ...

  8. CKEditor的使用方法

    CKEditor的使用方法 2014-03-31 09:44 8649人阅读 评论(1) 收藏 举报 版权声明:本文为博主原创文章,未经博主允许不得转载. ckeditor 的官方网站是 http:/ ...

  9. Orchard源码分析(4.2):Orchard.Logging.LoggingModule类

    与CollectionOrderModule一样,LoggingModule也是一个Autofac模块.它以属性注入的方式给需要日志服务的对象设置Logger.    如果一个类有Orchard.Lo ...

  10. jquery报错Uncaught ReferenceError: $ is not defined