實現跨域訪問的方法總結

解決跨域問題的經常使用方法

跨域, 是指調用方和被調用方的協議域名端口,不徹底相同(即不一樣源),跨域的訪問或調用會被瀏覽器禁止,於是跨域是瀏覽器端特有的,服務端無跨域問題。javascript

服務端代理

服務端代理,又稱爲請求轉發/反向代理php

原理:html

A站的頁面跨域訪問B站的服務端接口,A站請求本身的後端接口,A站的後端接口負責轉發請求到B站,並將B站的響應返回給A站頁面。前端

適用場景:java

調用第三方服務接口ajax

優勢:
比較通用,可解決大多數跨域問題json

缺點:
須要後端支持(提供轉發接口),增長本站服務器負擔,沒法維持session狀態後端

示例

系統A(A.com)對外提供一個郵件發送接口,可接收GET或POST方式傳遞參數(如email, subject, body等)數據。當系統b(b.com)須要使用到這個接口的功能時,並提供比較好的用戶體驗時,採起了經過ajax將用戶填入的信息傳遞數據給這個接口,來實現發送郵件的目的。api

系統b的頁面請求自身的後端接口proxy.php跨域

$.post(
    'http://b.com/proxy.php',
    {
        email:"impng@impng.com",
        subject:"ajax跨域之代理",
        body:"這裏是郵件內容"
    },
    function(data){
        if(data == 1) alert('發送成功!');
    }
);

proxy.php轉發請求到系統A

$post = $_POST;
$data = array(
            'email'     => $post['email'],
            'subject'   => $post['subject'],
            'body'      => $post['body'],
        );

$url = 'http://A.com/ISendMail.php?';   // 系統A的發送郵件接口
$url .= http_build_query($data);

$res = file_get_contents($url);
echo $res;

JSONP

原理:
因爲引用外部資源不受同源策略限制,如 <img> <link> <script>, 因此可經過動態插入<script>標籤, 訪問跨域的服務接口,只要跨域接口把數據組裝成函數調用的形式,前端頁面就能夠獲取到數據.

優勢:
徹底由前端控制,不須要後端支持,兼容性好。

缺點:
只能get方式(由於本質是加載js)

示例:

<!doctype html>

<html>
<head>
    <script>
    function loadContent() {
        var s = document.createElement('SCRIPT');
        s.src = 'http://www.anotherdomain.com/TestCrossJS.aspx?f=setDivContent'; //指定回調
        document.body.appendChild(s);
    }

    function setDivContent(v) {
        var dv = document.getElementById("dv");
        dv.innerHTML = v;
    }
    </script>
</head>

<body>
    <div id="dv"></div>
    <input value="Click Me">
</body>

</html>

// 其中的www.anotherdomain.com/TestCrossJS.aspx是這樣的,
<script runat="server">
void Page_Load(object sender, EventArgs e) {
    string f = Request.QueryString["f"];
    Response.Clear();
    Response.ContentType = "application/x-javascript";
    Response.Write(String.Format(@" {0}('{1}'); ", f, DateTime.Now)); //組裝函數調用表達式
    Response.End();
}
</script>

iframe監聽hash

在頁面內嵌或動態生成指向別的網站的IFRAME,而後這2個網頁間能夠經過改變對方的anchor hash fragment來傳輸消息。改變一個網頁的anchor hash fragment並不會使瀏覽器從新加載網頁,因此一個網頁的狀態得以保持,而網頁自己則能夠經過一個計時器(timer)來察覺本身anchor hash的變化(h5 window.onhashchange),從而獲取數據並做出響應。

父頁面

<!doctype html>
<html>

<head>
    <meta charset="utf8" />
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <title>Test iframe cross domain</title>
    <script>
    var url = "http://localhost:8081/cross_sub.html";
    var oldHash = null;
    var timer = null;

    function getHash() {
        var hash = window.location.hash;
        if ((hash.length >= 1) && (hash.charAt(0) == '#')) {
            hash = hash.substring(1); //得到#後的值
        }

        return hash;
    }

    function sendRequest() {
        var d = document; //簡寫document
        var t = d.getElementById('request');
        var f = d.getElementById('ifrm');
        f.src = url + "#" + t.value + (new Date().getTime());//修改iframe.src的hash
    }

    function setDivHtml(v) {
        var d = document;
        var dv = d.getElementById('response');
        dv.innerHTML = v;
    }

    function idle() {
        var newHash = getHash();

        if (newHash != oldHash) {
            setDivHtml(newHash);
            oldHash = newHash;
        }

        timer = window.setTimeout(idle, 100); // 遞歸實現循環 定時檢查自身hash的變化
    }

    window.onload = function (){
        timer = window.setTimeout(idle, 100);
    };

    window.name = 'parent';
    </script>
</head>
    
<!-- localhost:8080/cross_parent.html -->
<body>
    <h1>THIS IS THE PARENT PAGE</h1>
    <input type="text" id="request" />
    <input type="submit" value="send" id="send" />
    <div id="response" style="margin: 30px; padding: 30px; border:1px dashed #ccc;">
        
    </div>
    <iframe src="http://localhost:8081/cross_sub.html" frameborder="0" id="ifrm" style="border:5px solid #ddd; padding:20px; width:80%; margin:auto; display:block; height:300px; "></iframe>

    <script type="text/javascript">
        var sendBtn = document.getElementById('send');
        sendBtn.onclick = sendRequest;
    </script>
</body>

</html>

子頁面

<!doctype html>
<html>

<head>
    <meta charset="utf8" />
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <title>Test iframe cross domain</title>
    <script>
    var url = "http://localhost:8080/cross_parent.html";
    var oldHash = null;
    var timer = null;

    function getHash() {
        var hash = window.location.hash;
        if ((hash.length >= 1) && (hash.charAt(0) == '#')) {
            hash = hash.substring(1);
        }

        return hash;
    }

    function sendRequest() {
        var d = document;
        var t = d.getElementById('request');
        var f = parent;
        //alert(f.document); //試着去掉這個註釋,你會獲得「Access is denied」
        f.location.href = url + "#" + t.value +( new Date().getTime() ); //修改父窗口的hash
    }

    function setDivHtml(v) {
        var d = document;
        var dv = d.getElementById('response');
        dv.innerHTML = v;
    }

    function idle() {
        var newHash = getHash();

        if (newHash != oldHash) {
            setDivHtml(newHash);
            oldHash = newHash;
        }

        timer = window.setTimeout(idle, 100);
    }

    window.onload = function() {
        timer = window.setTimeout(idle, 100);
    };
    window.name = 'sub';
    </script>
</head>
<!-- localhost:8081/cross_sub.html -->

<body>
    <h1>THIS IS THE SUB PAGE</h1>
    <input type="text" id="request" />
    <input type="submit" value="send" id="send" />
    <div id="response" style="margin: 30px; padding: 30px; border:1px dashed #ccc;">
    </div>


    <script type="text/javascript">
        var sendBtn = document.getElementById('send');
        sendBtn.onclick = sendRequest;
    </script>

</body>

</html>

window.name

window 對象的name屬性是一個很特別的屬性,當該window的location變化,而後從新加載,它的name屬性能夠依然保持不變。那麼咱們能夠在頁面 A中用iframe加載其餘域的頁面B,而頁面B中用JavaScript把須要傳遞的數據賦值給window.name,iframe加載完成以後,頁面A修改iframe的地址,將其變成同域的一個地址,而後就能夠讀出window.name的值了。這個方式很是適合單向的數據請求,並且協議簡單、安全。不會像JSONP那樣不作限制地執行外部腳本。

父頁面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>window.name test</title>
</head>
<body>
    <button onclick="toggleIfrmSrc()">切換iframe src</button>
    <iframe  id="ifrm" src="http://localhost:9200/a.html" frameborder="0" style="width: 500px; height: 500px;"></iframe>
    <script>
        var $ = function(s ) {
            return document.querySelector(s);
        }

        function toggleIfrmSrc() {
            if (!oldUrl) {
                oldUrl = ifrm.src;
            }
            var isCross = /:9200/.test(ifrm.src);
            var newUrl = isCross ? '/a-same.html' : oldUrl;
            ifrm.src = newUrl;


            console.log('subwin.name:', subwin.name); // 獲取到
        }

        var oldUrl = '';

        var ifrm = $('#ifrm');
        var subwin = ifrm.contentWindow;
        console.log('subwin.name:', subwin.name); // 跨域,獲取不到


    </script>
</body>
</html>

子頁面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>window.name test </title>
</head>
<body>
    <h2>sub page, window.name</h2>
    <script>
    window.name = 'iamA';

    var c = 1;
    setInterval(function () {
        window.name += c;
    }, 2000);
    </script>
</body>
</html>

document.domain

僅僅是子域名不一樣,可改成同域。如:blog.exam.com, exam.com blog.examp.com/hello.html中,可設置 document.domain = 'exam.com';

window.postMessage

h5 api

CROS

需後端支持

相關文章
相關標籤/搜索