本文介绍了什么是跨域,以及常见的解决跨域的5种方法的具体解释,会举例分别解释每一种方法是怎么使用的。
目录
1.什么是跨域
2.解决跨域问题的方法
(1)JSONP(JSON with Padding)
(2)CORS(Cross-Origin Resource Sharing)
(3)跨文档消息传递(postMessage)
(4)Nginx代理跨域
(5)WebSocket
3.总结
跨域是一个浏览器安全策略,用于防止恶意网站通过 JavaScript 代码访问来自其他网站的资源。同源策略要求网页只能访问与其来源相同的资源,即具有相同的协议(如http或https)、域名和端口号。
例如,假设你的网站位于https://www.example.com,如果你在 JavaScript 中尝试通过XMLHttpRequest请求 https://www.google.com 的数据,这就是一个跨域请求,因为域名不同。
跨域请求默认会被浏览器阻止,因为这可能会导致安全问题。
利用script标签可以跨域请求资源的特性,通过动态创建script标签实现跨域请求。
工作原理如下:
① 前端页面通过动态创建标签,设置src属性为目标服务器API的URL,并同时指定一个回调函数的名称作为URL参数。
function handleResponse(data) {
// 处理返回的数据
}
let script = document.createElement('script');
script.src = 'http://api.example.com/data?callback=handleResponse';
document.body.appendChild(script);
② 目标服务器收到请求后,将数据封装在回调函数中返回给前端。回调函数的名称由前端传递的callback参数指定。
// 目标服务器处理请求,将数据封装在回调函数中返回
let data = { "name": "John", "age": 25 };
let callbackName = req.query.callback; // 假设通过query参数获取回调函数名称
res.send(callbackName + '(' + JSON.stringify(data) + ')');
③ 前端页面定义回调函数,当服务器返回数据时,会通过回调函数将数据传递给前端,从而实现跨域请求获取数据。
function handleResponse(data) {
// 处理返回的数据
console.log(data);
}
JSONP优势:在于它可以兼容老旧浏览器,并且相对简单易用。
但是需要注意的是,JSONP只支持GET请求,且需要目标服务器的支持,即目标服务器需要事先将数据封装在回调函数中返回给前端。
此外,JSONP存在安全风险,因为它会执行目标服务器返回的任意代码,所以需要确保目标服务器的可信性。最好在使用JSONP时确认目标服务器是否提供了相关支持并了解其安全性。
服务器设置响应头中的Access-Control-Allow-Origin字段,指定允许访问的域名,从而允许跨域请求。
假设有一个网页 http://example.com
,它试图通过 AJAX 请求从 http://api.lizi.com/data
获取数据,而这两个源(域名)不同,此时就出现了跨域问题。
以下是基于 CORS 的示例:
① 网页 http://example.com
发送 AJAX 请求:
请求地址:http://api.lizi.com/data
请求方法:GET
请求头中的 Origin 字段:http://example.com
② (被访问网址)服务器接收到 AJAX 请求后,返回响应头部信息:
Access-Control-Allow-Origin
字段:指定允许访问该资源的源地址。如果值设为 *
,则表示允许任意源的请求访问该资源。必填字段。
Access-Control-Allow-Methods
字段:指定允许的 HTTP 请求方法。可以根据实际需求来设定允许的方法。如果服务器没有设置这个字段,浏览器会默认只允许使用 GET、POST、HEAD 这三个方法进行跨域请求。
Access-Control-Allow-Headers
字段:指定允许的请求头字段,可自定义。
Access-Control-Expose-Headers
字段:指定响应中可以通过响应头访问的额外自定义响应头字段,可自定义。
Access-Control-Max-Age
字段:指定预检请求的缓存时间,即浏览器可以缓存该响应的时间。在指定时间段内,浏览器可以重复使用同样的预检请求结果,而无需再次发送预检请求。
响应头中的 Access-Control-Allow-Origin 字段:http://example.com
响应头中的 Access-Control-Allow-Methods 字段:GET, POST, OPTIONS
响应头中的 Access-Control-Allow-Headers 字段:Content-Type
响应头中的 Access-Control-Expose-Headers 字段:custom-header(自定义)
响应头中的 Access-Control-Max-Age 字段:3600
③ 若服务器允许该跨源请求,浏览器(http://example.com
)将接收到实际的响应数据:
响应头中的 Access-Control-Allow-Origin 字段:http://example.com
响应头中的 Access-Control-Expose-Headers 字段:custom-header
响应数据:请求所需的数据。
postMessage
是 JavaScript 浏览器 API 的一部分,是内置在浏览器中的,可以直接在 JavaScript 中使用。postMessage
方法可以在页面的上下文中使用,即在全局 window
对象上调用。通过该方法,可以向其他窗口或文档发送消息,并在目标窗口中接收和处理这些消息。
postMessage
方法的一般语法:
otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow
:目标窗口的引用,可以是通过 window.open
打开的弹出窗口对象、内嵌的 iframe
的 contentWindow
、或者通过 window.parent
引用的父窗口对象。message
:要发送的消息内容,可以是字符串、数字、布尔值、对象等。targetOrigin
:目标文档的源。可以是具体的源,如 'http://example.com'
,或者通配符 '*'
(表示任何源),或者不设置(默认为当前文档的源)。transfer
:可选参数,传递的 Transferable 对象(如 ArrayBuffer、MessagePort 等)数组。这些对象将被转移所有权,而不是复制。示例:
假设有两个页面:
Page A:http://a.example.com/ 和 Page B:http://b.example.com/ ,这两个页面不同源,需要通过 postMessage 进行跨文档通信。
在 Page A 中,使用以下代码向 Page B 发送消息:
const iframe = document.getElementById('iframeB');
const message = { type: 'sendData', data: 'Hello, Page B!' };
const targetOrigin = 'http://b.example.com';
iframe.contentWindow.postMessage(message, targetOrigin);
在 Page B 中,使用以下代码接收来自 Page A 的消息:
window.addEventListener('message', handleMessage);
function handleMessage(event) {
if (event.origin !== 'http://a.example.com') {
console.log('Unauthorized origin', event.origin);
return;
}
const message = event.data;
console.log('Received message', message.type, message.data);
// 如果是需要的消息类型,则做相应的处理
if (message.type === 'sendData') {
handleData(message.data);
}
}
function handleData(data) {
console.log('Data from Page A:', data);
}
postMessage
方法仅发送消息,而不提供直接的应答机制。接收方需要通过监听 message
事件,以及在事件处理函数中处理接收到的消息。
在自己的服务器上设置一个代理,将跨域请求发送到目标服务器,并将响应返回给客户端。
Nginx代理跨域涉及内容较多,会单独写文章来解释。
JSONP :
优点是简单易用,兼容性好,并且无需服务端支持,适用于简单的跨域处理。
缺点是只支持 GET 请求,无法处理复杂的跨域场景。
CORS:
优点是简单易用,能够处理复杂的跨域场景,适合做ajax各种跨域请求。
缺点是浏览器IE10以下不支持。
跨文档消息传递(postMessage):
优点是安全可靠,兼容性较好,并且能够处理较为复杂的跨域场景。
缺点是需要浏览器支持 HTML5。
Nginx代理跨域:
部署方便,可以通过简单的配置文件实现跨域处理。Nginx 代理还可以实现请求的负载均衡和缓存等高级功能。
适合前后端分离的前端项目调后端接口。