zoukankan      html  css  js  c++  java
  • 浏览器同源策略和跨域方法

    https://www.jianshu.com/p/1d0ee9bac639

    https://www.cnblogs.com/laixiangran/p/9064769.html

    http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

    http://www.ruanyifeng.com/blog/2016/04/cors.html

    浏览器同源策略及处理办法

    一、什么是浏览器的同源策略

    同源是指同协议同域名同端口

     
    同源策略.png

    注:IE 未将端口号加入到同源策略的组成部分之中

    浏览器同源策略的目的是为了保证用户信息的安全,防止恶意的网站窃取数据。如果网页之间不满足同源要求,将不能:

    1. 共享Cookie、LocalStorage、IndexDB
    2. 获取DOM
    3. AJAX请求不能发送

    二、既然知道这一策略,开发中如何规避呢?

    (1)通过设置window.domain的方式实现共享cookie

    这种方法适用于一级域名相同,二级域名不同的时候使用

    例如:现在有www.example.comexample.com两个网页,可以通过设置window.domain的形式来处理,前端页面可以通过设置window.domain=example.com或者后台设置Set-Cookie: key=value; domain=.example.com; path=/来实现共享cookie

    (2)通过修改片段标识符的方法实现跨域

    这种方法适用于iframe嵌套网页间的跨域。片段标识符就是URL的#号后面的部分。改变片段标识符,页面不会刷新。

    父窗口向子窗口传递信息

    //父窗口
    var src = originURL + '#' + data;
    document.getElementById('myIFrame').src = src;
    //子窗口通过监听hashchange事件回去父窗口传递的信息
    window.onhashchange = checkMessage;
    function checkMessage() {
      var message = window.location.hash;
      // ...
    }
    

    子窗口向父窗口传递信息

    //子窗口修改父窗口的片段标识符,同样,父窗口也可以通过hashchange事件来获取数据
    parent.location.href= target + "#" + hash;
    

    (3)使用window.name处理跨域问题

    window.name属性具有如下特点,无论是否同源,只要在同一窗口中设置了该属性,后一个网页就可以读取到。该方法借助window.name这一属性和iframe具有跨域能力的关键点,来处理跨域问题。

    首先,在localhost:8080服务器创建文件demo.html和空白文件proxy.html,然后,在localhost:8081服务器创建data.html文件,文件内容如下所示:

    //localhost:8080/demo.html
    <!doctype html>
    <html>
        <head>
            <title>demo</title>
        </head>
        <body>
            <h1>demo</h1>
            <iframe id="myIframe" width="400" height="400"></iframe>
            <script>
                var myIframe = document.getElementById('myIframe');
                myIframe.src = 'http://localhost:8081/yzdj-mobile-web/data.html';
                var state = 0;
                myIframe.onload = function(){
                    if(state === 1) {
                        console.log(myIframe.contentWindow.name);
                        myIframe.contentWindow.document.write('');
                        myIframe.contentWindow.close();
                    }else if(state === 0){
                        state = 1;
                        myIframe.contentWindow.location = 'http://localhost:8080/yzdj-admin-web/proxy.html';
                    }
                }
            </script>
        </body>
    </html>
    
    //localhost:8081/data.html
    <!doctype html>
    <html>
        <head>
            <title>data</title>
        </head>
        <body>
            <h1>data</h1>
            <script type="text/javascript">
                window.name = '{a:1,b:2}';
            </script>
        </body>
    </html>
    

    这种解决方法的优点是window.name容量大,缺点是:需要监听window.name变化,影响网页性能。

    (4)使用window.postMessage解决跨域

    这种方法借助HTML5的跨文档API(cross-document messaging)中增加的window.postMessage方法,允许跨窗口通信,不管窗口是否同源。父窗口和子窗口可以监听message事件,获取对方发送的消息。这种方法可以实现LocalStorage等信息的共享。

    messageevent对象提供三个属性:

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

    下面分别使用window.open的方法和嵌套iframe的方法实现父子窗口之间的通信
    使用window.open方法父页面:

    
    //localhost:8080
    <!doctype html>
    <html>
        <head>
            <title>admin</title>
        </head>
        <body>
            <h1 class="header">parent</h1>
            <div class="mb20">
                <textarea name="ta" id="data" cols="30" rows="5">hello world</textarea>
                <button style="font-size:20px;" onclick="send()">post message</button>
            </div>
            <script>
            var pop = window.open('http://localhost:8181/yzdj-mobile-web/demo.html');
            function send() {
                var data = document.querySelector('#data').value;
                pop.postMessage(data, 'http://localhost:8181/'); // 触发跨域子页面的messag事件
            }
            
            window.addEventListener('message', function(messageEvent) {
                var data = messageEvent.data; 
                console.info('message from child:', data);
            }, false);
            </script>
        </body>
    </html>
    

    子页面:

    //localhost:8081
    <!doctype html>
    <html>
        <head>
            <title>mobile</title>
        </head>
        <body>
            <h1 class="header">chidren</h1>
            <input type="text" id="inp" value="some contents..">
            <button onclick="send()">send</button>
            
            <script>
            var origin ='';
            var source = '';
            window.addEventListener('message', function(ev) {
                var data = ev.data;
                origin = ev.origin;
                source = ev.source
                console.info('message from parent:', data);
            }, false);
            
            function send() {
                var data = document.querySelector('#inp').value;
                source.postMessage(data, origin); // 若父页面的域名和指定的不一致,则postMessage失败
            }
            </script>
        </body>
    </html>
    

    使用iframe嵌套的方法,父页面

    <!doctype html>
    <html>
        <head>
            <title>parent</title>
        </head>
        <body>
            <h1 class="header">parent</h1>
            <div class="mb20">
                <textarea name="ta" id="data" cols="30" rows="5">hello world</textarea>
                <button style="font-size:20px;" onclick="send()">post message</button>
            </div>
            <!-- 跨域的情况 -->
            <iframe src="http://localhost:8081/b.html" id="child" style="display: block; border: 1px dashed #ccc; height: 300px;"></iframe>
            
            <script>
                function send() {
                    var data = document.querySelector('#data').value;
                    window.frames[0].postMessage(data, 'http://localhost:9022/'); // 触发跨域子页面的messag事件
                }
                
                window.addEventListener('message', function(messageEvent) {
                    var data = messageEvent.data; 
                    console.info('message from child:', data);
                }, false);
            </script>
        </body>
    </html>
    

    子页面

    <!doctype html>
    <html>
        <head>
            <title>chilren</title>
        </head>
        <body>
            <h1 class="header">chilren</h1>
            
            <input type="text" id="inp" value="some contents..">
            <button onclick="send()">send</button>
            
            <script>
                window.addEventListener('message', function(ev) {
                    var data = ev.data;
                    console.info('message from parent:', data);
                }, false);
                
                function send() {
                    var data = document.querySelector('#inp').value;
                    parent.postMessage(data, 'http://localhost:9011/'); // 若父页面的域名和指定的不一致,则postMessage失败
                }
            </script>
        </body>
    </html>
    

    (5)使用JSONP来实现AJAX的跨域

    定义和用法:通过动态插入一个script标签。浏览器对script的资源引用没有同源限制,同时资源加载到页面后会立即执行(没有阻塞的情况下)。

    特点:通过情况下,通过动态创建script来读取他域的动态资源,获取的数据一般为json格式。

    实例如下:

    <script>

        function testjsonp(data) {

           console.log(data.name); // 获取返回的结果

        }

    </script>

    <script>

        var _script = document.createElement('script');

        _script.type = "text/javascript";

        _script.src = "http://localhost:8888/jsonp?callback=testjsonp";

        document.head.appendChild(_script);

    </script>

    // 1. 定义一个 回调函数 handleResponse 用来接收返回的数据 
    function handleResponse(data) { console.log(data); };
    // 2. 动态创建一个 script 标签,并且告诉后端回调函数名叫 handleResponse
    var body = document.getElementsByTagName('body')[0];
    var script = document.gerElement('script');
    script.src = 'http://www.laixiangran.cn/json?callback=handleResponse';
    body.appendChild(script);
    // 3. 通过 script.src 请求 `http://www.laixiangran.cn/json?callback=handleResponse`,
    // 4. 后端能够识别这样的 URL 格式并处理该请求,然后返回 handleResponse({"name": "laixiangran"}) 给浏览器
    // 5. 浏览器在接收到 handleResponse({"name": "laixiangran"}) 之后立即执行 ,也就是执行 handleResponse 方法,获得后端返回的数据,这样就完成一次跨域请求了。

    或者使用jq的ajax方法

    $.ajax({
        url:'http://www.example.com/getData',
        dataType:'jsonp',
        jsonp:'callback',
        data:{
            wd:'XX.value'
        },
        success:function(result){
            alert(result.s);
        } ,
        error:function(err){
            alert(err)
        }
    });
    

    这种方法需要和设置修改请求网址的后台,代码中的callback需要和后台设置的一致

    优点

    • 使用简便,没有兼容性问题,目前最流行的一种跨域方法。

    缺点

    • 只支持 GET 请求。
    • 由于是从其它域中加载代码执行,因此如果其他域不安全,很可能会在响应中夹带一些恶意代码。
    • 要确定 JSONP 请求是否失败并不容易。虽然 HTML5 给 script 标签新增了一个 onerror 事件处理程序,但是存在兼容性问题。

    (6)webSocket

    是一种通信协议,使用ws://wss://作为协议前缀,这种协议不受同源政策的影响,只要服务器支持就可以。

    (7)CORS(Cross-Origin Resource Sharing,跨源资源分享)

    它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了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
    

    不同时满足上面条件的就是非简单请求。

    对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段,用来说明本次请求的是哪个源。服务器根据这个字段来判断是否允许请求。

    非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
    对于非简单请求,会在正式通信之前,增加一次HTTP查询请求,称为预检(preflight)请求。

    服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

    优点

    • CORS 通信与同源的 AJAX 通信没有差别,代码完全一样,容易维护。
    • 支持所有类型的 HTTP 请求。

    缺点

    • 存在兼容性问题,特别是 IE10 以下的浏览器。
    • 第一次发送非简单请求时会多一次请求。

    (8)还有一种方法是使用Nginx转发。

  • 相关阅读:
    String和enum的互相转换
    LeetCode: Sort Colors
    LeetCode: Subsets II
    LeetCode: Spiral Matrix II
    LeetCode: Subsets
    LeetCode: Sum Root to Leaf Numbers
    LeetCode: Sqrt(x)
    LeetCode: Sudoku Solver
    LeetCode: Spiral Matrix
    LeetCode: Substring with Concatenation of All Words
  • 原文地址:https://www.cnblogs.com/leftJS/p/10947299.html
Copyright © 2011-2022 走看看