同源策略与跨域

在浏览器中,有一个很重要的安全性限制,被称为 “Same-Origin Policy”(同源策略)。通过 XHR 实现 Ajax 通信的一个主要限制,就来自与同源策略。即默认情况下,XHR 对象只能访问与包含它的页面位于同一个域中的资源。然而,当进行一些比较深入的前端编程的时候,不可避免地需要进行跨域操作。这时候“同源策略”就显得过于苛刻,于是,就需要进行跨域访问数据了!

一、同源策略?

同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能。所谓同源就是指两个页面拥有相同的协议(protocol),主机 IP*(域名)端口(如果指定),那么这两个页面就属于同一个源(origin)*。

下图说明了大部分的同源策略检测机制的情况,可做参考:

同源策略检测机制

二、为什么要有同源策略?

举个栗子:

比如一个黑客程序,他利用 <iframe> 把真正的银行登录页面嵌到他的页面上。当你使用真实的用户名、密码登录时,他的页面就可以通过 Javascript 读取到你的表单中 input 中的内容。这样用户名、密码就轻松到手了。

同源策略就为了防止上述事情的发生。如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web 是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

三、如何跨域?

同源策略控制了不同源之间的交互,例如在使用 XMLHttpRequest ,则会受到同源策略的约束。

浏览器同源策略并不是对所有的请求均制约:

  1. 制约:XMLHttpRequest
  2. 不制约:<img><iframe><script> 等具有 src 属性的标签;

处理 Ajax 跨域问题主要有以下 4 种方式:

  1. 利用 JSONP
  2. 利用 <iframe>
  3. 利用代理
  4. 跨域资源共享(CORS)

1. JSONP

HTML DOM 中,<script> 标签是可以跨域访问服务器上的数据的。因此,可以指定 <script>src 属性为跨域的 url,从而实现跨域访问。利用在页面中创建 <script> 节点,向不同域提交 Http 请求的方法称为 JSONP,这项技术可以解决跨域提交 Ajax 请求的问题。

下面,我们来看一下示例代码:

前端代码:

function doResponse(data){      // 数据请求成功时的回调
    console.log('开始处理响应数据');
    console.log(data);
}

$('#bt3').click(function(){     //JSONP方式
    var script = document.createElement('script');
    script.type = "text/javascript";
    script.src="http://localhost/AJAX_day08/5.php?callback=doResponse";
    document.head.appendChild(script);
});

服务器端代码:

header('Content-Type: application/javascript');     // 相应的请求头
$cb = $_REQUEST['callback'];
$arr = ['ename'=>'King', 'age'=>50];
$str = json_encode($arr);
echo $cb.'(' .$str. ')' ;       // 利用传过来的函数名作为回调,将所需数据作为参数传入

顺便分享下,jQuery 中两个可以发起 JSONP 请求的函数:

  1. $.getJSON()

    1. 普通 XHR 请求:$.getJSON('x.php', data, fn);
    2. JSONP 请求:$.getJSON('x.php?callback=?',data, fn);
  2. $.ajax();

    1. 普通 XHR 请求:

      $.ajax({
          url: 'x.php',
          data: {'uname':'tom'},
          success: fn
      });
    2. JSONP 请求:

      $.ajax({
          url: 'x.php',
          data: {'uname':'tom'},
          dataType: 'jsonp',  // 指定服务器端返回jsonp的数据的类型
          success: fn
      });
      • JSONP 的优点:

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

        1. 只支持 GET 请求而不支持 POST 等其它类型的 Http 请求;
        2. 只支持跨 Http 协议请求的域,不能解决不同协议的域的两个页面之间如何进行 JavaScript 调用的问题。

2. <iframe>location.hash

原理是利用 location.hash 来进行传值,因为改变 hash 并不会导致页面刷新,所以可以利用 hash 值来进行数据传递。

注释:在 url:http://a.com#helloword 中的 #helloworld 就是 location.hash

举个栗子:
假设域名 a.com 下的文件 cs1.html 要和 cnblogs.com 域名下的 cs2.html 传递信息。
首先 cs1.html 自动创建一个隐藏的 <iframe><iframe>src 指向 cnblogs.com 域名下的 cs2.html 页面,这时的 hash 值可以做参数传递用。
cs2.html 响应请求后,再将通过修改 cs1.htmlhash 值来传递数据(由于两个页面不在同一个域下,IE、Chrome 不允许修改 parent.location.hash 的值,所以要借助于 cnblogs.com 域名下的一个代理 <iframe>;Firefox 可以修改)
同时在 cs1.html 上加一个定时器,隔一段时间来判断 location.hash 的值有没有变化。一点有变化,则获取 hash 值。代码如下:

先是 a.com 下的文件 cs1.html 文件:

function startRequest(){
    var ifr = document.createElement('iframe');
    ifr.style.display = 'none';
    ifr.src = 'http://www.cnblogs.com/lab/cscript/cs2.html#paramdo';
    document.body.appendChild(ifr);
}

function checkHash() {
    try {
        var data = location.hash ? location.hash.substring(1) : '';
        if (console.log) {
            console.log('Now the data is '+data);
        }
    } catch(e) {};
}
setInterval(checkHash, 2000);

再是 cnblogs.com 域名下的 cs2.html:

switch(location.hash){      // 模拟一个简单的参数处理操作
    case '#paramdo':
        callBack();
        break;
    case '#paramset':
        //do something……
        break;
}

function callBack(){
    try {
        parent.location.hash = 'somedata';
    } catch (e) {
        // ie、chrome的安全机制无法修改parent.location.hash,
        // 所以要利用一个中间的cnblogs域下的代理iframe
        var ifrproxy = document.createElement('iframe');
        ifrproxy.style.display = 'none';

        // 注意该文件在"a.com"域下
        ifrproxy.src = 'http://a.com/test/cscript/cs3.html#somedata';
        document.body.appendChild(ifrproxy);
    }
}

最后 a.com 下的域名 cs3.html

//因为parent.parent和自身属于同一个域,所以可以改变其location.hash的值
parent.parent.location.hash = self.location.hash.substring(1);
  • <iframe>的优点:

    1. 可以解决完全跨域情况下的脚本置换问题;
  • <iframe>的缺点:

    1. 传递的数据容量、类型都是有限的
    2. 数据直接暴露在了 url 中,不安全

3. 代理

原理:用户访问 A 网站时所产生的对 B 网站的跨域访问请求,均提交到 A 网站的代理页面( Post 页面过去)。由该页面代替用户页面完成交互,从而返回合适的结果。

  • 代理的优点:

    1. 可以解决现阶段所能够想到的多数跨域访问问题
  • 代理的缺点:

    1. 要求 A 网站提供 Web 代理的支持。因此 A 网站与 B 网站之间必须是紧密协作的;
    2. 每次交互过程,A 网站的服务器负担增加
    3. 无法代用户保存 session 状态

4. 跨域资源共享(CORS)

新版本的 XMLHttpRequest 对象,可以向不同域名的服务器发出 Http 请求。这叫做”跨域资源共享”(Cross-origin resource sharing,简称CORS)CORS背后的基本思想,就是使用自定义的 Http 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。

举个栗子:
一个简单的使用 GETPOST 发送的请求,它没有自定义的头部,而主题内容是 text/plain。在发送该请求时,需要给他附加一个额外的 Origin 头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予相应。比如:

Origin: http://www.nczonline.net

如果,服务器认为这个请求可以接受,那么就在 Access-Control-Allow-Origin 头部中回发相同的源信息(如果是公共资源,可以回发 *。以上述例子为例,服务器端则要进行这些设置:

header('Access-Control-Allow-Origin: http://www.nczonline.net');     // 上述例子
header('Access-Control-Allow-Origin:*');        // 公共资源

header('Access-Control-Allow-Methods:POST,GET');

如果没有这个头部,或者有这个头部但源信息不匹配,浏览器就会驳回请求。

  • 跨域资源共享(CORS)的优点:

    1. 代码的写法与不跨域的请求完全一样。
  • 跨域资源共享(CORS)的缺点:

    1. 浏览器必须支持这个功能,且服务器端必须同意这种”跨域”。
    2. 请求和响应都不包含 cookie 信息。
申明:该文的撰写,主要是由于近期面试的时候,问的这块比较多,自己又比较生疏。所以阅读了一些文章,并撰写了此文。该文的主体框架是借鉴了 Mervyn Zhang的这篇文章。但原文的排版、对 <iframe>CORS 的介绍欠妥,于是,我改了很多,望请勿怪!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值