跨域及其解决办法

216 阅读5分钟

hokage1.jpg 一、为什么会出现跨域问题 出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)。 通俗讲就是当你在本地调试的时候,网页地址为localhost:8080, 而后端的接口地址又为例如:test.prot.com 此类新地址,这时若你想在本地调试正确访问接口地址并拿到接口数据,就会出现跨域问题。(localhost:8080与test.prot.com非同源地址)

二、什么是跨域 当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

当前页面url被请求页面url是否跨域是否跨域
www.test.com/www.test.com/index.html同源(协议、域名、端口号相同)
www.test.com/www.test.com/index.html跨域协议不同(http/https)
www.test.com/www.baidu.com/跨域主域名不同(test/baidu)
www.test.com/blog.test.com/跨域子域名不同(www/blog)
www.test.com:8080/www.test.com:7001/跨域端口号不同(8080/7001)

三、非同源限制 【1】无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB 【2】无法接触非同源网页的 DOM 【3】无法向非同源地址发送 AJAX 请求

四、解决方法

  1. webpack本地代理,即proxy反向代理 正常情况下:网页地址为:localhost:8080, 接口地址为localhost:8080/prm/...,此时两者为同源地址,但却不是接口真实地址,而接口真实地址为类似test.prot.com/prm/... , 所以此时对接口地址进行处理为真实地址,须在webpack的devServer中设置代理,如下:
 devServer: {
        proxy: {
            "/prm": {
              target: "https://test.prot.com",
              changOrigin: true,
            }
        }
    }

代理之后的接口地址变为真实地址test.prot.com/prm/... 但是在浏览器的network的header中我们可以看到,接口地址仍为localhost:8080/prm/... image.png

解释: 这就是为什么我们可以实现跨域访问接口,因为在浏览器中接口地址与网页地址都为localhost:8080 是同源地址,可以实现互相请求,但实际上接口地址在发送给服务端接口的地址缺位真实地址test.prot.com/prm/... ,以此实现了跨域来访问接口。

  1. JSONP JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求。 核心思想:网页通过添加一个
<script src="http://test.com/data.php?callback=dosomething"></script>
// 向服务器test.com发出请求,该请求的查询字符串有一个callback参数,用来指定回调函数的名字
 
// 处理服务器返回回调函数的数据
<script type="text/javascript">
    function dosomething(res){
        // 处理获得的数据
        console.log(res.data)
    }
</script>

3. Nginx反向代理

Nginx 实现原理类似于 Node 中间件代理,需要你搭建一个中转 nginx 服务器,用于转发请求。

使用 nginx 反向代理实现跨域,是最简单的跨域方式。只需要修改 nginx 的配置即可解决跨域问题,支持所有浏览器,支持 session,不需要修改任何代码,并且不会影响服务器性能。

我们只需要配置nginx,在一个服务器上配置多个前缀来转发http/https请求到多个真实的服务器即可。这样,这个服务器上所有url都是相同的域 名、协议和端口。因此,对于浏览器来说,这些url都是同源的,没有跨域限制。而实际上,这些url实际上由物理服务器提供服务。这些服务器内的 javascript可以跨域调用所有这些服务器上的url。

先下载nginx,然后将 nginx 目录下的 nginx.conf 修改如下:

server {
 
    #nginx监听所有localhost:8080端口收到的请求
	listen       8080;
	server_name  localhost;
 
	# Load configuration files for the default server block.
	include /etc/nginx/default.d/*.conf;
    #localhost:8080 会被转发到这里
	#同时, 后端程序会接收到 "192.168.25.20:8088"这样的请求url
	location / {
		proxy_pass http://192.168.25.20:8088;
	}
	#localhost:8080/api/ 会被转发到这里
    #同时, 后端程序会接收到 "192.168.25.20:9000/api/"这样的请求url
	location /api/ {
		proxy_pass http://192.168.25.20:9000;
	}
	error_page 404 /404.html;
		location = /40x.html {
	}
	error_page 500 502 503 504 /50x.html;
		location = /50x.html {
	}
}

4.CORS 在服务端设置Access-Control-Allow-Origin:* / 指定地址

  1. 跨文档通信 API:window.postMessage()

调用postMessage方法实现父窗口test1.com向子窗口http://test2.com发…

它可用于解决以下方面的问题:

页面和其打开的新窗口的数据传递 多窗口之间消息传递 页面与嵌套的iframe消息传递 上面三个场景的跨域数据传递

// 父窗口打开一个子窗口
var openWindow = window.open('http://test2.com', 'title');
 
// 父窗口向子窗口发消息(第一个参数代表发送的内容,第二个参数代表接收消息窗口的url)
openWindow.postMessage('Nice to meet you!', 'http://test2.com');

调用message事件,监听对方发送的消息

// 监听 message 消息
window.addEventListener('message', function (e) {
  console.log(e.source); // e.source 发送消息的窗口
  console.log(e.origin); // e.origin 消息发向的网址
  console.log(e.data);   // e.data   发送的消息
},false);

6. 将浏览器设置为可跨域(经常使用且最为直接的方法) 将浏览器设置为可跨域状态,设置方法为: Chrome: (1)关闭所有的chrome浏览器窗口 (2)新建一个chrome快捷方式,右键“属性”,“快捷方式”选项卡里选择“目标”,添加 --disable-web-security --user-data-dir=C:\MyChromeDevUserData --allow-file-access-from-files --enable-vertical-tabs --ignore-certificate-errors C:\MyChromeDevUserData 是你本地硬盘的一个目录,最好自己新建一个,上面的目录路径换成自己新建的目录即可 image.png (3)重新启动chrome

此时的接口地址需要在一开始就设置为真实的地址如:<https://test.prot.com/prm>... ,    因为此时浏览器已不存在跨域的同源限制, 可在浏览器中直接访问真实接口。

本文部分借鉴blog.csdn.net/qq_38128179…