欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

跨域

程序员文章站 2022-07-10 19:56:59
...

一、何为跨域

跨域是指从一个域名的网页去请求另一个域名的资源。比如从www.a.com 页面去请求 www.b.com 的资源。但是一般情况下不能这么做,它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。跨域的严格一点的定义是:只要 协议,域名,端口有任何一个的不同,就被当作是跨域。

请注意:localhost和127.0.0.1虽然都指向本机,但也属于跨域。

二、为何有跨域限制

安全问题:如果一个网页可以随意地访问另外一个网站的资源,那么就有可能在客户完全不知情的情况下出现安全问题。比如下面的操作就有安全问题:
a. 用户访问 www.taobao.com ,登陆并进行网银操作,这时cookie啥的都生成并存放在浏览器;
b. 不小心访问了一个恶意的网站
c. 该网站就可以在它的页面中,拿到银行的cookie,比如用户名,登陆token等,然后发起对www.taobao.com 的操作。
d. 如果这时浏览器不予限制,并且银行也没有做响应的安全处理的话,那么用户的信息有可能就这么泄露了。

三、为什么要跨域

既然有安全问题,那为什么又要跨域呢? 有时公司内部有多个不同的子域,比如一个是 a.company.com , 而应用是放在 b.company.com , 这时想从 b.company.com去访问 a.company.com 的资源就属于跨域。何况现在前后端分离,有时api路径要实现跨域。

四、解决跨域

1.跨域共享(cros)

CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。
只需要在后台中加上响应头来允许域请求!在被请求的Response header中加入以下设置,就可以实现跨域访问了!

//指定允许其他域名访问
'Access-Control-Allow-Origin:*'//或指定域
//响应类型
'Access-Control-Allow-Methods:GET,POST'
//响应头设置
'Access-Control-Allow-Headers:x-requested-with,content-type'
2.jsonp

JSONP是JSON with Padding(填充式json)的简写,是应用JSON的一种新方法,只不过是被包含在函数调用中的JSON,例如:

callback({"name","Jack"});

JSONP的原理:通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。(即用javascript动态加载一个script文件,同时定义一个callback函数给script执行而已。)

在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的。 例如:

有个a.html页面,它里面的代码需要利用ajax获取一个不同域上的json数据,假设这个json数据地址是http://example.com/data.php,那么a.html中的代码就可以这样:

<script type="text/javascript">
    function dosomething(jsondata){
        //处理获得的json数据
    }
</script>
<script src="http://example.com/data.php?callback=dosomething"></script>

js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。

<?php
$callback = $_GET['callback'];//得到回调函数名
$data = array('a','b','c');//要返回的数据
echo $callback.'('.json_encode($data).')';//输出
?>

最终,输出结果为:dosomething([‘a’,‘b’,‘c’]);

jquery

function corsTest(){
    $.ajax({
        type: "get",
        url: "http://other.host/data.json",
        dataType:"jsonp",
        jsonp:"callback",
        jsonpCallback:"callBack",
        ......
    })
}
function callBack(data){
    // do something
    ......
}

CORS和JSONP对比

  • 1、 JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
  • 2、 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
  • 3、 JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS)。
3. nginx 代理跨域
server {
    listen 8080;                         # 监听的端口
    server_name  192.168.1.1;            # 配置访问域名
    root  /data/toor;                    # 站点根目录
    error_page 502 404 /page/404.html;   # 错误页面
    location ^~ /api/  {                        # 使用 /api/ 代理 proxy_pass 的值
        proxy_pass  http://192.168.20.1:8080;    # 被代理的应用服务器 HTTP 地址
    }
}

复制代码以上简单的配置就可以实现反向代理的功能,跨域的问题也就解决了。

在 Vue 中就可以使用 proxyTable 这个属性进行相关的配置来解决跨域问题带来的烦恼。配置如下:

proxyTable: {
    '/weixin': {
        target: 'http://192.168.48.11:8100/', // 接口的域名
        secure: false,      // 如果是 https 接口,需要配置这个参数
        changeOrigin: true, // 如果接口跨域,需要进行这个参数配置
        pathRewrite: {
            '^/weixin': ''
        }
    },
},
    devServer: {
      port: 8888, // 端口号
      host: 'localhost',
      https: false, // https:{type:Boolean}
      open: true, //配置自动启动浏览器
      // proxy: 'http://localhost:4000' // 配置跨域处理,只有一个代理
    proxy: {
       '/api': {
            target: '<url>',
            ws: true,
            changeOrigin: true
       },
    }, // 配置多个代理
    }
4. 通过修改document.domain来跨子域

浏览器都有一个同源策略,其限制之一就是第一种方法中我们说的不能通过ajax的方法去请求不同源中的文档。 它的第二个限制是浏览器中不同域的框架之间是不能进行js的交互操作的。
不同的框架之间是可以获取window对象的,但却无法获取相应的属性和方法。比如,有一个页面,它的地址是http://www.example.com/a.html , 在这个页面里面有一个iframe,它的src是http://example.com/b.html, 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的:

<script type="text/javascript">
    function test(){
        var iframe = document.getElementById('ifame');
        var win = document.contentWindow;//可以获取到iframe里的window对象,但该window对象的属性和方法几乎是不可用的
        var doc = win.document;//这里获取不到iframe里的document对象
        var name = win.name;//这里同样获取不到window对象的name属性
    }
</script>
<iframe id = "iframe" src="http://example.com/b.html" onload = "test()"></iframe>

这个时候,document.domain就可以派上用场了,我们只要把http://www.example.com/a.html 和 http://example.com/b.html 这两个页面的document.domain都设成相同的域名就可以了。但要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。例如:a.b.example.com 中某个文档的document.domain 可以设成 a.b.example.comb.example.com 、example.com中的任意一个,但是不可以设成 c.a.b.example.com,因为这是当前域的子域,也不可以设成 baidu.com,因为主域已经不相同了。

1.在页面 http://www.example.com/a.html 中设置document.domain:

<iframe id = "iframe" src="http://example.com/b.html" onload = "test()"></iframe>
<script type="text/javascript">
    document.domain = 'example.com';//设置成主域
    function test(){
        alert(document.getElementById('iframe').contentWindow);//contentWindow 可取得子窗口的 window 对象
    }
</script>

2.在页面 http://example.com/b.html 中也设置document.domain:

<script type="text/javascript">
    document.domain = 'example.com';//在iframe载入这个页面也设置document.domain,使之与主页面的document.domain相同
</script>

修改document.domain的方法只适用于不同子域的框架间的交互。

5. 使用window.name来进行跨域

window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。

跨域
跨域
跨域
跨域
上面的例子中,我们用到的页面a.html和b.html是处于同一个域的,但是即使a.html与b.html处于不同的域中,上述结论同样是适用的,这也正是利用window.name进行跨域的原理。

6.HTML5的window.postMessage方法跨域

window.postMessage(message,targetOrigin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。
调用postMessage方法的window对象是指要接收消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 * 。

//a.html
    <script>
        function onLoad(){
            var iframe = document.getElementById('iframe')
            var win = iframe.contentWindow;
            win.postMessage("我是来自页面a的数据")
        }
    </script>
        <iframe src="http://localhost:5500/b.html" frameborder="0" id="iframe" onload="onLoad()"></iframe>
//b.html
    <script>
        window.onmessage = function(e){
            e = e || event;
            alert(e.data)
        }
    </script>

跨域

7. web sockets

web sockets是一种浏览器的API,它的目标是在一个单独的持久连接上提供全双工、双向通信。(同源策略对web sockets不适用)
web sockets原理:在js创建了web socket之后,会有一个HTTP请求发送到浏览器以发起连接。取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为web sockt协议。
只有在支持web socket协议的服务器上才能正常工作。

var socket = new WebSockt('ws://www.baidu.com');//http->ws; https->wss
socket.send('hello WebSockt');
socket.onmessage = function(event){
    var data = event.data;
}
8.图像ping(单向)

图像ping是与服务器进行简单、单向的跨域通信的一种方式,请求的数据是通过查询字符串的形式发送的,而相应可以是任意内容,但通常是像素图或204相应(No Content)。 图像ping有两个主要缺点:首先就是只能发送get请求,其次就是无法访问服务器的响应文本。

var img = new Image();
img.onload = img.onerror = function(){
alert("done!");
};
img.src = "https://avatars0.githubusercontent.com/u/43026870?s=460&v=4";
document.body.insertBefore(img,document.body.firstChild);
        </script>
相关标签: 跨域 js