实现跨域访问的方法总结

解决跨域问题的常用方法

跨域, 是指调用方和被调用方的协议域名端口,不完全相同(即不同源),跨域的访问或调用会被浏览器禁止,因而跨域是浏览器端特有的,服务端无跨域问题。

服务端代理

服务端代理,又称为请求转发/反向代理

原理:

A站的页面跨域访问B站的服务端接口,A站请求自己的后端接口,A站的后端接口负责转发请求到B站,并将B站的响应返回给A站页面。

适用场景:

调用第三方服务接口

优点:
比较通用,可解决大多数跨域问题

**缺点: **
需要后端支持(提供转发接口),增加本站服务器负担,无法维持session状态

示例

系统A(A.com)对外提供一个邮件发送接口,可接收GET或POST方式传递参数(如email, subject, body等)数据。当系统b(b.com)需要使用到这个接口的功能时,并提供比较好的用户体验时,采取了通过ajax将用户填入的信息传递数据给这个接口,来实现发送邮件的目的。

系统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

需后端支持

posted @ 2018-02-05 14:04  stephenykk  阅读(759)  评论(0编辑  收藏  举报