跨域解决方案

作者: FWHeart | 来源:发表于2017-10-04 17:45 被阅读0次

    跨域解决方案

    跨域解决方案有:设置document.domain,使用带src标签,JSONP,navigation对象,CORS,window.postMessage,片段标识符,window.name,WebSocket

    设置document.domain

    • 原理:相同主域名不同子域名下的页面,可以设置document.domain让它们同域
    • 限制:同域document提供的是页面间的互操作,需要载入iframe页面
    // URL http://a.com/foo
    var ifr = document.createElement('iframe');
    ifr.src = 'http://b.a.com/bar'; 
    ifr.onload = function(){
        var ifrdoc = ifr.contentDocument || ifr.contentWindow.document;
        ifrdoc.getElementsById("foo").innerHTML);
    };
    
    ifr.style.display = 'none';
    document.body.appendChild(ifr);
    

    需要设置iframe的domain,将 document.domain往上设置一级,这样即可操作DOM和Cookie

    document.domain = 'a.com'
    

    使用带src标签

    • 原理:所有具有src属性的HTML标签都是可以跨域的,包括<img>, <script>,<iframe>
    • 限制:只能用于GET方法

    JSONP

    利用script标签可以跨域这点,跨域获得的脚本包含一个客户端和服务器端约定好的回调函数,以及服务器端发送的数据。

    jQuery实现

    //URL具有callback参数时, jQuery将会把它解释为一个JSONP请求,创建一个<script>标签来完成该请求。
    $.getJSON( "http://b.a.com/bar?callback=callback", function( data ){
        // 处理跨域请求得到的数据
    });
    

    JS实现

    function loadJsonp(url,callback){
        var script = document.createElement('script'),
        rand = Math.random().toString().substring(2, 8),
        functionName = "getJsonStr" + rand;
    
        script.src = url + "?callback=" + functionName;
    
        window[functionName] = function(data) {
            if (callback) {
                callback(data);
            }
            try {
                delete window[functionName];
            } catch (e) {
                window[functionName] = undefined;
            }
        };
    
        var head = document.getElementsByTagName('head')[0];
        script.onload = function() {
            script.onload = undefined;
            head.removeChild(script);
        };
        script.onerror = function(e) {
            console.error(e);
        };
    
        head.appendChild(script);
    }
    

    跨域资源共享(CORS)

    • 原理:服务器设置Access-Control-Allow-OriginHTTP响应头之后,浏览器将会允许跨域请求,H5推荐的跨域方式。
    • 限制:浏览器需要支持HTML5,可以支持POST,PUT等方法

    跨域发送Cookie

    将xhr的属性withCredentials设置为true后,即可携带目标域的Cookie

    // 原生
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.withCredentials = true;
    xhr.send();
    // jQ
    $.ajax({
       url: a_cross_domain_url,
       xhrFields: {
          withCredentials: true
       }
    });
    

    还需要服务器端设置Access-Control-Allow-Credentials响应头为true,并且将Access-Control-Allow-Origin设置为请求对应的域名

    既然Access-Control-Allow-Origin只允许单一域名, 服务器可能需要维护一个接受 Cookie 的 Origin 列表, 验证 Origin 请求头字段后直接将其设置为Access-Control-Allow-Origin的值。 (这一实践来自 Stackoverflow) 值得注意的是在 CORS 请求被重定向后 Origin 头字段会被置为 null。 此时可以选择从Referer头字段计算得到Origin。

    preflight

    对于非简单请求,CORS 机制跨域会首先进行 preflight(一个 OPTIONS 请求), 该请求成功后才会发送真正的请求。 这一设计旨在确保服务器对 CORS 标准知情,以保护不支持 CORS 的旧服务器。

    preflight.png

    window.postMessage

    • 原理:HTML5允许窗口之间发送消息
    • 限制:浏览器需要支持HTML5,获取窗口句柄后才能相互通信

    这是一个安全的跨域通信方法,postMessage(message,targetOrigin)也是HTML5引入的特性。 可以给任何一个window发送消息,不论是否同源。第二个参数可以是*但如果你设置了一个URL但不相符,那么该事件不会被分发。

    // 页面A,URL: http://a.com/foo
    var win = window.open('http://b.com/bar');
    win.postMessage('Hello, bar!', 'http://b.com'); 
    // 页面B,URL: http://b.com/bar
    window.addEventListener('message',function(event) {
        console.log(event.data);
    });
    

    片段标识符

    • 原理:改变网页#后面的部分,被改变的网页可以通过监听onhashchange事件得到通知
    • 限制:URL具有长度的限制,只能传送字符串
    // A窗口
    var src = originURL + '#' + data;
    document.getElementById('myIFrame').src = src;
    // B窗口
    window.onhashchange = function(){
        var message = window.location.hash;
    }
    

    window.name

    • 原理:只要在同一个窗口里,前一个网页设置了这个属性,后一个网页可以读取它。
    • 限制:必须监听子窗口window.name属性的变化,影响网页性能。

    WebSocket

    • 原理:WebSocket使用ws、wss作为通信协议,改协议没有同源策略
    • 限制:需要浏览器支持

    参考文献

    JS高程(第三版)
    Web开发中跨域的几种解决方案
    浏览器同源策略及其规避方法
    CORS 跨域发送 Cookie
    CORS 跨域中的 preflight 请求

    相关文章

      网友评论

        本文标题:跨域解决方案

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