同源策略和跨域知识点学习

问题原由是在使用weibo api的时候,发现有一个报错。weibo api是https协议,我本地是模拟的回调域名,而后进行数据通讯,本地http协议,因而乎就报错了。出于对postMessage的不是很熟悉,借此机会学习晚上一些本身的知识储备。
php

api.weibo.com/2/oauth2/authorize?client_id=******&response_type=token&d…ansport=html5&referer=http://www.unofficial.cn/demo/vuejs/demo.html:1 Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://www.unofficial.cn') does not match the recipient window's origin ('http://www.unofficial.cn').

同源策略

在这以前须要先熟悉一下这个概念,同源指请求协议相同,主机名相同,端口相同,涉及安全的策略。css

// 例如个人博客地址
    http://www.unofficial.cn/demo/postMessage/pm1.html      同
    http://www.unofficial.cn/demo/vuejs/index.html          同
    https://www.unofficial.cn/demo/postMessage/pm1.html     不一样 协议不一样
    http://blog.unofficial.cn/demo/postMessage/pm1.html     不一样 主机名不一样
    http://www.unofficial.cn:8080/demo/postMessage/pm1.html 不一样 端口不一样

容许跨域写

表单提交,例如我模拟了一个表单提交到个人一个其它站点。
同源策略主要限制的是不一样源之间的交互操做,对于跨域内嵌的资源不受该策略限制。html

容许跨域嵌入

  • <script src="……"> 标签嵌入脚本,语法错误信息只能在同源脚本中捕捉到(?)。
  • <link rel="stylesheet" href="……"> 标签嵌入css
  • <img src="" alt=""> 标签嵌入图片
  • <video> 和 标签嵌入多媒体资源
  • @font-face
  • <iframe src="……" frameborder="0"> 载入的任何资源。可使用x-frame-options消息头来阻止这种形式的交互。

不容许跨域读

须要注意的是,页面内的引入的文件的域并不重要,重要的是加载该文件的页面所在的域。例如说我在博客的首页引入了 //cdn.bootcss.com/jquery/3.1.1/jquery.min.js 的jquery文件,这时 jquery.min.js 的源应该就是个人博客地址 http://www.unofficial.cn 。前端

iframe

同域可读可写,跨域可读不可写vue

// 请求地址://www.unofficial.cn/demo/postmessage/pm2.html
<iframe src="pm2.html" frameborder="0"></iframe>
<iframe src="//blog.unofficial.cn/demo/postmessage/pm2.html" frameborder="0"></iframe>
<script>
    window.onload = function() { // 必须等待文档加载结束才能获取
        var iframe = document.getElementsByTagName('iframe');
        console.log(iframe[0].contentDocument); // 同源
        console.log(iframe[1].contentDocument); // 不一样源
    }
</script>
// 不一样源时使用contentWindow/contentDocument报错
// pm1.html:12 Uncaught DOMException: Failed to read the 'contentDocument' property from 'HTMLIFrameElement': Blocked a frame with origin "http://www.unofficial.cn" from accessing a cross-origin frame.(…)
  • 同源
    iframe外部操做,主要经过contentDocument/contentWindow,iframe内部使用window.parent,若是只是嵌套了一层可使用window.top,iframe多层嵌套可使用window.frameElement
// 外部 -> 内

    var iframe = document.getElementsByTagName('iframe');
    // 举例第一个
    iframe[0].contentDocument.getElementById('test').innerText = 123;

    // 内部 -> 外
    window.parent.getElementById('test').innerText = 123;
  • 跨域
    若是须要在跨域的状况下传递参数怎么操做呢?
    iframe内部操做,主要经过 location.hash
// 外部传递一个123给内部
    var src = iframe[0].src;
    iframe[0].src = src.indexOf('#') != -1 ? src.split('#')[0].concat('#', 123) : src.concat('#', 123);
    // 而后内部监测hashChange,自动获取hash值

    // 内部更改hash
    window.location.hash = 123;
    // 可是如何外部如何监控src的变化呢?

ajax

cors

同域可读可写,跨域请求不能检查到 Access-Control-Allow-Origin 的状况下会被拦截。html5

// www.unofficial.cn:4000
    // 跨域请求
    var url = "http://www.unofficial.cn/demo.php";
    var params = "lorem=ipsum&name=binny";

    var http = new XMLHttpRequest();
    http.open("POST", url, true);
    
    http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

    http.onreadystatechange = function() {
        if(http.readyState == 4 && http.status == 200) {
            alert(http.responseText);
        }
    }
    http.send(params);

XMLHttpRequest cannot load http://www.unofficial.cn/demo.php. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://www.unofficial.cn:4000' is therefore not allowed access.jquery

上面错误提示能够设置 Access-Control-Allow-Origin ,因而在header中添加设置便可实现跨域请求。ajax

options

  • Access-Control-Allow-Origin
    origin参数指定一个容许向该服务器提交请求的URI.对于一个不带有credentials的请求,能够指定为'*',表示容许来自全部域的请求。
    Access-Control-Allow-Origin: http://www.unofficial.cnjson

  • Access-Control-Allow-Credentials
    它的值是一个布尔值,表示是否容许发送Cookie。默认是 true 容许的。 『实际测试没发现,也许是方法还不对吧。』后端

  • Access-Control-Expose-Headers
    设置浏览器容许访问的服务器的头信息的白名单。若是没有设置白名单的,默认状况下只能获取 Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma的值,没设置返回 null,不然会获得如下提示:

    Refused to get unsafe header "X-Powered-By"

    例如:
    // 服务端设置 Access-Control-Expose-Headers: X-Powered-By

    前端能够这样获取到 X-Powered-By 的属性值

    var http = new XMLHttpRequest();
    
        http.getResponseHeader('X-Powered-By'); //
  • Access-Control-Max-Age
    设置预请求时间。便是设置 OPTION 的时间。

  • Access-Control-Allow-Methods
    设置容许的请求方法。

jsonp

cors的方式能够发起post请求,或者说其它形式的请求,可是jsonp只能使用get的方式获取数据。

<script>
    function abc(data) {
        console.log(data);
    }abc('{"abc":"123"}');
</script>

<script src="http://www.unofficial.cn/test/demo.php?callback=abc"></script>

简单说就是定义好回调处理方法,把回调函数的名称传递给后端,后端拿到数据名称后返回会的数据就是对于回调方法的执行。

<script src="http://www.unofficial.cn/test/demo.js"></script>
/**
 * demo.js的内容
 * abc({"abc":"123"});
 */

什么是postMessage

postMessage是window对象的一个属性,widow.postMessage是一个安全的跨源通讯协议。当且仅当执行脚本的页面使用相同的协议(一般都是 http)、相同的端口(http默认使用80端口)和相同的 host(两个页面的 document.domain 的值相同)时,才容许不一样页面上的脚本互相访问。 window.postMessage 提供了一个可控的机制来安全地绕过这一限制,当其在正确使用的状况下。

  • iframe的状况下咱们能够这样使用,等待页面加载结束时传参数到指定源。
    ```
    // localhost ① pm1.html页面中存在一个跨域iframe引用

    // www.unofficial.cn pm2.html中咱们跨域监听 message 获取 postmessage 传过来的数据。

    ```

  • window.open的状况下就须要特殊处理一下了
    // localhost ② window.open <script> function openAPage() { // 同源的状况下能够判断页面是否加载结束 var openPage = window.open('//localhost/demo/postMessage/pm2.html'); openPage.onload = function() { openPage.postMessage('some messages', '*'); } // 不一样源的状况下,使用setTimeout或者setInterval var openPage = window.open('//www.unofficial.com/demo/postMessage/pm2.html'); setTimeout(function() { openPage.postMessage('some messages', '*'); }, 0) } </script>

    延迟多长时间执行?页面加载时间是多长,这个不是很好判断,setTimeout须要略估计一个时间,待open的页面加载完成了再postMessage(应该不比彻底加载就能够postMessage了)。要否则就是定时器,定时推一次,直接触发事件后使用window.opener取消定时器。

总结

问题基本都是在过程当中发现一个学习一个,对于没有太多场景的学习,只能这样慢慢积累。

参考资料

  • https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy
相关文章
相关标签/搜索