一、为什么会出现跨域问题
跨域问题是由浏览器的同源策略引起的。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响,同源策略限制了从一个源加载的文档或脚本如何与来自另一个源的资源进行交互,这是为了防止恶意脚本获取用户的敏感信息或进行其他攻击。
导致跨域问题的原因主要包括:
- 同源策略: 浏览器限制了不同源之间的交互,确保恶意脚本不能轻易获取用户敏感信息。
- AJAX/XMLHttpRequest/Fetch API方式的跨域请求: 当使用这些方式发起请求时,同源策略会生效,导致跨域问题。
二、什么是跨域
同源策略要求协议、域名、端口完全相同才能进行交互,若任何一个不同,就会被视为跨域。
当前页面URL | 被请求页面的URL | 是否产生跨域 | 原因 |
http://www.stxs.com | http://www.stxs.com/index.html | 否 | 同源(协议、域名、端口相同) |
http://www.stxs.com | https://www.stxs.com/index.html | 跨域 | 协议不同 |
http://www.stxs.com | http://www.taobao.com | 跨域 | 域名不同 |
http://www.stxs.com:8080/ | http://www.stxs.com:8081/ | 跨域 | 端口号不同 |
三、跨域解决方法
1、JSONP
JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求,并且因为是通过<script>标签
来获取数据,对于一些错误没有办法处理。
核心思想:网页通过添加一个<script>标签
,向服务器请求 JSON 数据,服务器收到请求后,将数据放在一个指定名字的回调函数(这个回调函数的名字需要我们前后端进行协商,共同定义的参数位置传回来)
代码示例:
<script src="http://www.xtxs.com?callback=gettingdata"></script>
// 向服务器发出请求,该请求包含一个callback参数,用来指定回调函数的名字
// 处理服务器返回回调函数的数据
<script type="text/javascript">
function gettingdata(res){
// 获得的数据
console.log(res.data)
}
</script>
2、CORS
跨域资源共享(Cross-Origin Resource Sharing) 是一种基于 HTTP 头的机制,用于允许服务器标识除了自身以外的其他源(域、协议或端口),使得浏览器能够安全地访问这些源的资源。它其实跟我们前端没有关系,只需要我们服务端,对Access-Control-Allow-Origin属性进行设置就能够解决跨域问题,因为浏览器在检测到存在这个属性的时候,就不会进行拦截,从而不会产生跨域。
3、window.postMessage()
它是一种在不同窗口或文档间实现安全跨源通信的 API。主要特点包括:
- 实现跨源通信: 允许不同窗口或文档之间发送消息,突破同源策略限制。
- 安全性: 仅当目标窗口的 origin 符合预期,消息才会被发送,提供了一种受控制的跨文档通信方式。
代码示例:
// 父窗口打开一个子窗口
let openWindows = window.open('http://stxs.com', 'new');
// 父窗口调用postMessage用来传递数据(第一个参数代表发送的内容,第二个参数代表接收消息窗口的url)
openWindows.postMessage('传递的数据', 'http://stxs.com');
子窗口调用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);
4、websocket
Websocket 是 HTML5 的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket 和 HTTP 都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 服务器与 客户端都能主动向对方发送或接收数据。同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。
5、Nginx反向代理
Nginx 实现原理类似于中间件代理,需要你搭建一个中转 nginx 服务器,用于转发请求。使用 nginx 反向代理实现跨域,是最简单的跨域方式。只需要修改 nginx 的配置即可解决跨域问题,支持所有浏览器,支持 session,不需要修改任何代码,并且不会影响服务器性能。
代码实例:
# 监听80端口,处理来自前端的HTTP请求
server {
listen 80;
server_name frontend.example.com;
# 配置反向代理
location / {
proxy_pass http://backend_server; # 将请求代理到后端服务器
proxy_set_header Host $host; # 传递原始请求的Host头信息
proxy_set_header X-Real-IP $remote_addr; # 传递原始请求的客户端IP信息
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 传递原始请求的客户端IP列表
proxy_set_header X-Forwarded-Proto $scheme; # 传递原始请求的协议信息
}
# 额外配置可根据需求添加,比如SSL/TLS配置、缓存配置等
}
6、Proxy代理
Proxy代理通过在同源域下设置代理,实现跨域请求。浏览器只对同源策略生效,代理在同源下,故能规避跨域限制。我们需要配置代理服务器,前端请求发送到代理服务器,再由代理服务器转发请求。因为我们那个浏览器机制针对的是浏览器,它对服务器端没有任何的限制,我们只需要把发送请求交给服务器来进行,让服务器之间进行交互,这样也能拿到我们想要的数据,从而解决跨域问题。
代码实例:vue项目,在项目种创建vue.config.js文件
module.exports = {
devServer:{
proxy:{
'/api':{ ///api 表示将拦截以/api开头的请求路径
target:'http://localhost:3000', //跨域的域名
// ws: true, //是否代理websocked
changeOrigin: true, //是否开启跨域
pathRewrite:{ //重写路径
'^/api':'' //把/api替换为空字符
}
}
}
}
}
四、总结
面试官:谈谈你对跨域的理解,如何解决跨域问题?
我们可以这样回答:
跨域是指在不同的页面之间进行数据传输或通信产生的,严格一点说:只要 协议,域名,端口有任何一个的不同,就当作是跨域。跨域是浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。这是为了防止恶意脚本获取用户的敏感信息或进行其他攻击。(what)
我们也有很多的解决办法去解决跨域,比如说我们可以使用JSONP来解决跨域,
它利用了script标签的src属性能够跨越访问资源的这种机制来实现跨越,它就是在我们的请求地址中携带一个参数(一个函数名),这个参数是前端定义好的一个函数,这个函数接收的参数,就是后端返回给我们的数据,而我们的后端呢,将我们这个url地址中的函数进行拆分,拼接上我们所需要的数据最后返回给我们。通过这样一种方式实现跨越,需要我们前后端进行约定,但是这种方法也存在的很多的缺点,(how)
比如说,
1、JSONP只支持GET的请求,同时JSONP容易遭受到攻击,
2、JSONP是通过script标签加载数据,对于一些错误是没有办法处理,而且JSONP这种方式比较古老现在已经很少用了,比如说淘宝之类的可能还在使用,
第二种方式:使用CORS(Cross-Origin Resource Sharing),它其实跟我们前端没有关系,只需要我们服务端,对Access-Control-Allow-Origin属性进行设置就能够解决跨域问题,因为浏览器在检测到有这个属性的时候,就不会进行拦截了。
当然我们也可以使用proxy代理的一种方式,因为我们那个浏览器机制针对的是浏览器,它对服务器端没有任何的限制,我们只需要把发送请求交给服务器来进行,让服务器之间进行交互,这样也能拿到我们想要的数据,这几种的方法,也是我们比较常见跨域的解决方案,相比于第一种方法我们用的比较多的还是第二种和第三种方法,我们也可以用其他的方式比如说Nginx反向代理,或者使用HTML5的window.postMessage()方法进行跨越资源的访问。