当前位置: 首页 > 工具软件 > GNE-JS > 使用案例 >

前端js跨域请求访问解决方案

仇承志
2023-12-01

 什么是不同域?

协议、域名、端口有任何一个不同,都被当作是不同的域

 什么是跨域?

Web 浏览器具有一个称为相同站点源策略的安全策略,此策略可阻止网页访问另一个域中的数据。  网站通常会让其服务器在后端请求其他站点服务器中的内容,由此避开浏览器中的检查,从而绕开此策略。
注意好多文章写的是 “JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。”  这根本就是错误的,大家依然在抄来抄去,即使是一些所谓专业前端网站也是直接抄上。

 什么是前端跨域?

通过前端方案绕开浏览器的安全策略,在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架(iframe)中的数据。

 前端跨域请求访问解决方案有哪些?

同源策略跨域(document.domain)

适用于主域相同而子域不同的情况
具体的做法是在http://www.51xuediannao.com/javascript/kuayu.html和http://web.51xuediannao.com/thread-111.htm两个页面头部<head>区域文件中分别加上<script> document.domain = "51xuediannao.com";</script>

利用iframe和location.hash

使用此方法来完全跨域试试:比如:1.com下的1.com/1.html和2.com域下的2.com/2.html来通讯,来操作一下,
在1.com/1.html加入一个iframe,src="2.com/2.html",这样1.com/1.html就可以获取到iframe src上的hash值。
但是浏览器是不允许跨域修改iframe内的跨域域名通过parent.location.hash的值,
所以2.com/2.html并不能通过parent.location.hash修改hash值,这样2.com/2.html有变化是不能通过hash值来通知1.com/1.html,
于是我们要在2.com/2.html里面再加一个iframe src="1.com/2.html",然后让src="1.com/2.html"通过parent.parent.location.hash来修改1.com/1.html那个iframe的hash。
1.com/1.html想知道hash是不是有变化还要加个定时检测,
真的很绕很麻烦,我们还是看看下面还有没有更好的办法吧。

window.name实现的跨域数据传输

有三个页面:
 
a.com/app.html:应用页面。
a.com/proxy.html:代理文件,一般是一个没有任何内容的html文件,需要和应用页面在同一域下。
b.com/data.html:应用页面需要获取数据的页面,可称为数据页面。
实现起来基本步骤如下:
 
在应用页面(a.com/app.html)中创建一个iframe,把其src指向数据页面(b.com/data.html)。
数据页面会把数据附加到这个iframe的window.name上,data.html代码如下:
<script type="text/javascript">
    window.name = 'I was there!';    // 这里是要传输的数据,大小一般为2M,IE和firefox下可以大至32M左右
    // 数据格式可以自定义,如json、字符串
</script>
在应用页面(a.com/app.html)中监听iframe的onload事件,在此事件中设置这个iframe的src指向本地域的代理文件(代理文件和应用页面在同一域下,所以可以相互通信)。app.html部分代码如下:
<script type="text/javascript">
    var state = 0, 
    iframe = document.createElement('iframe'),
    loadfn = function() {
        if (state === 1) {
            var data = iframe.contentWindow.name;    // 读取数据
            alert(data);    //弹出'I was there!'
        } else if (state === 0) {
            state = 1;
            iframe.contentWindow.location = "http://a.com/proxy.html";    // 设置的代理文件
        }  
    };
    iframe.src = 'http://b.com/data.html';
    if (iframe.attachEvent) {
        iframe.attachEvent('onload', loadfn);
    } else {
        iframe.onload  = loadfn;
    }
    document.body.appendChild(iframe);
</script>
获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问)。
<script type="text/javascript">
    iframe.contentWindow.document.write('');
    iframe.contentWindow.close();
    document.body.removeChild(iframe);
</script>
总结起来即:iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。

JSONP跨域(支持get不支持post)

浏览器虽然不让跨域共享数据,但是不限制js资源跨域共享,跨域外链的js是可以执行的,同时也可以获取当前页面上已经。
先看一段代码:
a.com/1.html有如下js:
<script>
//回调函数
function callback(data) {
    alert(data.message);
}
</script>
<script src="http://a.com/test.js"></script>
a.com/test.js中执行 callback({message:"success"}); a.com/1.html中的函数callback(data)将会被执行,
于是,如果服务端返回一个类似这样的内容,也是可以出现同样的效果。
实际应用:
jqueryz中的$.getJSON
<div id="images"></div>
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?", function(data){
  $.each(data.items, function(i,item){
    $("<img/>").attr("src", item.media.m).appendTo("#images");
    if ( i == 3 ) return false;
  });
});
注意jsoncallback=?,服务端返回的函数名就是获取这个参数的值,当然参数名是前后端商定的,函数名就是他传啥就返回啥,因为你的api给别人用还要定死人家的执行的函数名应该是额昂人比较蛋疼的

以php大概演示如下:
<?php
$jsondata = json_encode(array('a' =>'b'));
$callback = $_REQUEST['jsoncallback'];
echo "(function(){var _jsonData=$jsondata; $callback(_jsonData);})()";

使用HTML5 postMessage

用postMessage支持基于web的实时消息传递,
浏览器支持情况:Chrome 2.0+、Internet Explorer 8.0+, Firefox 3.0+, Opera 9.6+, 和 Safari 4.0+ 

otherWindow.postMessage(message, targetOrigin);
otherWindow: 对接收信息页面的window的引用。可以是页面中iframe的contentWindow属性;window.open的返回值;通过name或下标从window.frames取到的值。
message: 所要发送的数据,string类型。
targetOrigin: 用于限制otherWindow,“*”表示不作限制
 
a.com/index.html中的代码:
<iframe id="ifr" src="b.com/index.html"></iframe>
<script type="text/javascript">
window.onload = function() {
    var ifr = document.getElementById('ifr');
    var targetOrigin = 'http://b.com';  // 若写成'http://b.com/c/proxy.html'效果一样
                                        // 若写成'http://c.com'就不会执行postMessage了
    ifr.contentWindow.postMessage('I was there!', targetOrigin);
};
</script>
 
b.com/index.html中的代码:
<script type="text/javascript">
    window.addEventListener('message', function(event){
        // 通过origin属性判断消息来源地址
        if (event.origin == 'http://a.com') {
            alert(event.data);    // 弹出"I was there!"
            alert(event.source);  // 对a.com、index.html中window对象的引用
                                  // 但由于同源策略,这里event.source不可以访问window对象
        }
    }, false);
</script>

CORS(Cross-Origin-Resource-Shares)跨域资源共享(服务端方法)

解决跨域问题,也可以在后端服务器直接设置header内容Access-Contrl-Allow-Origin,
 
PHP设置CORS:
<?php
header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Max-Age: 1000');
header('Access-Control-Allow-Headers: Content-Type');
 
Apache配置方法:
Header set Access-Control-Allow-Origin *  
以上配置的含义是允许所有的域发起的请求都可以获取该服务器上的资源,当然这样的配置有很大的安全问题,很容易遭受XSS(跨站脚本攻击)。可以只允许某个域访问我们的服务器,如下设置:
 
Header set Access-Control-Allow-Origin http://www.51xuediannao.com/
对于通配符*,它可以直接Access-Control-Allow-Origin:*这样使用,但它是不允许通配子域名的,如:Access-Control-Allow-Origin:*.51xuediannao.com

 类似资料: