一、为什么会出现跨域问题
出于浏览器的同源策略限制。同源策略(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 请求
四、解决方法
- 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/...
解释: 这就是为什么我们可以实现跨域访问接口,因为在浏览器中接口地址与网页地址都为localhost:8080 是同源地址,可以实现互相请求,但实际上接口地址在发送给服务端接口的地址缺位真实地址test.prot.com/prm/... ,以此实现了跨域来访问接口。
- 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:* / 指定地址
- 跨文档通信 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 是你本地硬盘的一个目录,最好自己新建一个,上面的目录路径换成自己新建的目录即可
(3)重新启动chrome
此时的接口地址需要在一开始就设置为真实的地址如:<https://test.prot.com/prm>... , 因为此时浏览器已不存在跨域的同源限制, 可在浏览器中直接访问真实接口。