终结同源策略!轻松实现跨域访问的9种方式!
跨域指的是在Web浏览器中,一个站点的脚本试图向另一个站点的脚本发起HTTP请求,这种请求是由于浏览器遵守同源策略(Same-Origin Policy)而被拒绝的。为了解决这个问题,我们可以使用以下9种方式:
1. JSONP:通过动态创建<script>标签向其他域请求数据,并将数据作为回调函数参数传递回来。
当需要在一个网页中访问另一个域名下的数据时,由于浏览器的同源策略会阻止这种访问,因此就需要使用JSONP来实现跨域数据的访问。
JSONP(JSON with Padding)是一种实现跨域网络数据传输的技术,它利用了<script>标签的跨域访问特性。
使用JSONP的基本过程如下:
- 在客户端代码中定义一个全局的回调函数,用于接收服务器返回的数据。
- 构建一个动态创建
示例代码如下:
function jsonpRequest(url, callback) {
// 创建<script>标签
var script = document.createElement('script');
// 构建URL地址,指定回调函数名称
var callbackName = 'jsonp_callback_' + Math.floor(Math.random() * 100000);
url += '&callback=' + callbackName;
// 在window对象上定义回调函数
window[callbackName] = function(data) {
// 处理服务器返回数据
callback(data);
// 删除回调函数
delete window[callbackName];
// 移除<script>标签
document.body.removeChild(script);
};
// 设置<script>标签的src属性
script.src = url;
// 添加<script>标签到元素中
document.body.appendChild(script);
}
通过调用这个jsonpRequest函数,我们可以跨域请求数据,并将数据传递给回调函数进行处理。
2. CORS:在服务器端设置响应头信息,允许跨域访问。
CORS(Cross-Origin Resource Sharing)是一种允许跨域访问资源的机制,它通过在服务器端设置响应头信息来允许跨域访问。
使用CORS的基本过程如下:
- 在服务器端设置响应头信息,允许指定的跨域请求访问资源。通过设置Access-Control-Allow-Origin头信息,指定允许访问的源,比如设置为"*"表示允许任意源访问。
- 浏览器在发送跨域请求时,会在请求头中添加Origin头信息,这个头信息指示了请求的源地址。
- 服务器在接收到请求后,会根据Origin头信息,在响应头中添加Access-Control-Allow-Origin头信息,以表明该源地址被允许访问。
示例代码如下:
// 使用Node.js的Express框架实现CORS跨域访问
const express = require('express');
const app = express();
// 设置允许跨域访问的资源
app.use((req, res, next) => {
// 设置允许跨域的源
res.setHeader('Access-Control-Allow-Origin', '*');
// 设置允许跨域的请求头
res.setHeader('Access-Control-Allow-Headers', 'origin, content-type, accept');
// 设置允许跨域的请求方法
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
// 继续处理请求
next();
});
// 处理API请求
app.get('/api', (req, res) => {
// 返回JSON数据
res.json({
code: 0,
message: 'success',
data: {
name: 'JSONP',
description: '使用JSONP实现跨域数据访问'
}
});
});
// 启动服务器
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
在这个示例代码中,我们使用了Node.js的Express框架实现了一个简单的API,该API允许跨域访问,通过设置响应头信息,指定了允许跨域请求访问的源、请求头和请求方法。客户端代码可以使用XMLHttpRequest或Fetch API来访问该API,并获得返回的JSON数据。
3. WebSocket:采用全双工通信方式,无需提前建立HTTP连接,可以跨域通讯。
WebSocket是一种基于TCP协议实现的全双工通信协议,它采用了一种类似HTTP的握手机制,在建立连接后,双方可以进行自由的数据传输,可以跨域通信,无需提前建立HTTP连接。
使用WebSocket的基本过程如下:
- 客户端通过WebSocket对象创建WebSocket连接,指定WebSocket服务端的地址。
- WebSocket服务端接收到连接请求后,进行握手处理,并建立起连接。在握手时,服务端会告知客户端是否支持WebSocket协议。
- 如果握手成功,客户端和服务端就可以进行双向数据传输,客户端和服务端均可发送和接收数据。
示例代码如下:
// 创建WebSocket连接
var socket = new WebSocket('ws://localhost:8080');
// 连接成功时,打印日志信息
socket.onopen = function() {
console.log('WebSocket connection is opened.');
};
// 接收到消息时,打印消息内容
socket.onmessage = function(event) {
console.log('WebSocket received message:', event.data);
};
// 连接关闭时,打印日志信息
socket.onclose = function() {
console.log('WebSocket connection is closed.');
};
// 发送消息
socket.send('Hello WebSocket!');
在这个示例中,我们使用WebSocket对象创建了一个WebSocket连接,并指定了WebSocket服务端的地址,然后通过事件监听函数来处理连接状态、接收消息和发送消息。客户端和服务端之间可以发送和接收数据,实现实时通信的功能。由于WebSocket协议采用了类似HTTP的握手机制,所以WebSocket可以跨域通信,无需提前建立HTTP连接。
4.postMessage:HTML5提供了一个专门用于跨文档通信的API,可以跨域通信。
HTML5提供了一个专门用于跨文档通信的API——postMessage。postMessage方法可以在不同的窗口(或文档)之间传递数据,包括字符串和对象等数据类型,可以实现跨域通信。
使用postMessage的基本过程如下:
- 在发送消息的窗口中,使用postMessage方法向指定的窗口发送消息,指定窗口的origin属性(协议、域名、端口号)以便接收窗口验证消息是否来自信任的源。
- 在接收消息的窗口中,通过添加message事件监听函数来接受消息,事件对象包含了发送窗口的信息、消息数据等。
示例代码如下:
// 发送消息
var sendMessage = {
type: 'text',
content: 'Hello, postMessage!'
};
var targetWindow = window.opener;
var targetOrigin = '*';
targetWindow.postMessage(sendMessage, targetOrigin);
// 接收消息
window.addEventListener('message', function(event) {
if (event.origin !== 'http://example.com') {
return;
}
console.log(event.data);
}, false);
在这个示例中,我们在发送消息的窗口中,使用postMessage方法向指定的窗口发送消息,指定了接收窗口的origin,以保证消息来自可信任的源。在接收消息的窗口中,添加message事件监听函数来接收消息,通过验证消息来自可信任的源,然后使用事件对象来获取消息数据。通过这种方式实现了从一个窗口向另一个窗口发送跨域消息,并实现了跨域通信。
5. 代理服务器:通过在服务器端代理请求,将跨域请求转到同一服务器,再返回到请求端。
代理服务器是指在服务器端转发请求和响应的中间层服务器,它可以代理客户端向目标服务器发送请求,获取响应,从而实现跨域请求。
使用代理服务器实现跨域请求的基本过程如下:
- 客户端向代理服务器发送请求。
- 代理服务器接收到请求后,将请求转发给目标服务器,获取响应。
- 代理服务器将响应返回给客户端,完成跨域请求。
示例代码如下:
// 在Node.js中使用http-proxy中间件实现代理服务器
const http = require('http');
const httpProxy = require('http-proxy');
// 创建代理服务器
const proxy = httpProxy.createProxyServer({});
// 处理请求
const server = http.createServer((req, res) => {
console.log('Proxy request:', req.url);
// 设置响应头,允许跨域访问
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'origin, content-type, accept');
// 如果是API请求,将请求转发到目标服务器
if (req.url.startsWith('/api')) {
proxy.web(req, res, {
target: 'http://localhost:3000'
});
} else {
res.end();
}
});
// 监听端口,启动服务器
server.listen(8000, () => {
console.log('Server is running on port 8000');
});
在这个示例中,我们使用Node.js自带的http模块创建了一个HTTP服务器,通过设置响应头信息,实现了允许跨域访问。在处理请求时,我们通过判断请求URL,将API请求转发到目标服务器,使用http-proxy中间件实现了代理服务器的功能,将请求转发到同一服务器上,再将响应返回给客户端,从而实现了跨域请求的目的。
6. nginx反向代理:利用nginx的反向代理特性来实现跨域。
Nginx是一款高性能的Web服务器和反向代理服务器,通过配置nginx的反向代理功能,可以解决跨域问题。
使用nginx反向代理实现跨域请求的基本步骤如下:
- 安装nginx,并编辑配置文件。
- 在nginx配置文件中,使用location指令匹配需要代理的URL,并设置代理服务器的地址和端口号。
- 重启nginx,使配置文件生效。
示例代码如下:
# 将请求 /api/ 转发到 http://localhost:3000/
location /api/ {
# 设置允许跨域访问
add_header 'Access-Control-Allow-Origin' '*';
# 设置需要代理的目标服务器
proxy_pass http://localhost:3000/;
}
在这个示例中,我们使用nginx反向代理功能,将请求URL为/api/的请求转发到http://localhost:3000/服务器,实现了跨域请求。同时,我们在nginx配置文件中设置了
Access-Control-Allow-Origin头信息,允许跨域访问,从而实现了跨域通信的目的。
7. iframe嵌套:利用iframe的跨域通信特性,在不同的iframe之间实现数据交换。
iframe是HTML中的一个重要元素,可以在一个网页中嵌入另一个网页或文档。iframe具有访问不同域名网页的能力,因此可以在不同iframe之间实现跨域通信。
使用iframe嵌套实现跨域通信的基本过程如下:
- 在主页面中定义一个iframe元素,并设置src属性为跨域页面的URL。
- 在跨域页面中,通过window.parent来获取主页面的window对象,并向主页面发送消息。
- 在主页面中,通过监听message事件来接收来自iframe的消息。
示例代码如下:
在主页面嵌入iframe:
主页面
主页面
<script>
// 监听消息事件,接收来自iframe的消息
window.addEventListener('message', function(event) {
console.log(event.data);
}, false);
</script>
在跨域页面发送消息:
跨域页面
<script>
// 发送消息给主页面
window.parent.postMessage('Hello, parent window!', '*');
</script>
跨域页面
在这个示例中,我们在主页面中嵌入了一个跨域的iframe,并监听message事件来接收来自iframe的消息。在跨域的iframe中,通过window.parent.postMessage()方法发送消息给主页面,'*'表示不限制发送消息的域名,接收哪个窗口也是如此。
使用iframe嵌套实现跨域通信时,需要注意的是,需要确保主页面和iframe页面的信任关系,避免信息泄露和CSRF攻击。
8. Document.domain:在同一个父域名下的不同子域名之间,通过设置document.domain来实现跨域访问。
在同一个父域名下的不同子域名之间进行跨域操作时,由于浏览器的同源策略,会限制JS代码的访问权限,为了解决这个问题,可以在父域名相同的情况下,通过设置document.domain来实现跨域访问。
具体实现方式是,在不同的子域名中,将document.domain设置为相同的父域名,如将子域名A.domain.com和子域名B.domain.com中的document.domain都设置为domain.com。这样,这两个子域名就可以通过JS代码来共享cookie、iframe等资源了。
需要注意的是,document.domain只能设置为包含它的域,例如,它可以设置为domain.com或sub.domain.com,但不能设置为otherdomain.com。并且,不同子域名之间的跨域操作仅适用于协议、端口号相同的情况。
下面是一个简单的JS代码示例:
假设当前页面的URL是
https://sub1.example.com/index.html,想要在同域下的
https://sub2.example.com/other.html页面中,访问当前页面的cookie。
- 在当前页面(https://sub1.example.com/index.html)中,设置document.domain:
document.domain = 'example.com';
- 在同域下的另一个页面(https://sub2.example.com/other.html)中,通过JS代码来访问当前页面的cookie:
// 获取当前域下的cookie
var cookies = document.cookie;
console.log(cookies);
通过将两个页面的document.domain设置为相同的父域名,使得它们处于同一个域中,从而实现了跨域访问。
9. 跨域资源共享(CORS)标准:CORS机制通过一些特定的HTTP头部来告诉浏览器,哪些跨域请求是可以被允许的,从而实现跨域数据访问。
CORS(Cross-origin Resource Sharing)是一个W3C标准,用于解决通过HTTP进行跨域访问的问题。
CORS机制允许服务器向浏览器发送附加HTTP头,告诉浏览器哪些跨域请求是被允许的。
具体来说,当浏览器向跨域服务器请求数据时,服务器可以在响应头部中加入
Access-Control-Allow-Origin字段。例如,如果A域名的页面向B域名的服务器请求资源,B服务器可以在响应头中添加以下字段:
Access-Control-Allow-Origin: https://A-domain.com
这个字段告诉浏览器A域名下的页面可以跨域访问B域名服务器的资源。如果没有设置这个字段,跨域请求将会被浏览器阻止。
除了
Access-Control-Allow-Origin字段,CORS机制还可以通过其他一些HTTP头部实现更复杂的跨域请求。例如,
Access-Control-Allow-Methods字段指定了服务器支持的HTTP方法,
Access-Control-Allow-Headers指定了服务器接受的请求头,
Access-Control-Allow-Credentials指定了请求是否需要使用凭证等等。
下面是一个简单的CORS示例代码,在服务器端需要添加
Access-Control-Allow-Origin头部。
// 定义CORS允许访问的域名
const ALLOW_ORIGIN = 'https://example.com'
const http = require('http');
http.createServer(function(req, res) {
console.log('request method:', req.method);
console.log('request url:', req.url);
console.log('request headers:', req.headers);
if (req.method === 'OPTIONS') {
// 处理预检请求
res.writeHead(200, {
'Access-Control-Allow-Origin': ALLOW_ORIGIN,//此处指定了允许访问的域名
'Access-Control-Allow-Methods': 'POST,GET,OPTIONS',
'Access-Control-Allow-Headers': 'X-Requested-With,Content-Type',
'Access-Control-Allow-Credentials': 'true',
'Content-Length': 0
});
res.end();
} else {
// 处理CORS请求
res.writeHead(200, {
'Access-Control-Allow-Origin': ALLOW_ORIGIN,//此处指定了允许访问的域名
'Access-Control-Allow-Credentials': 'true',
'Content-Type': 'application/json'
});
res.write(JSON.stringify({'msg': 'Hello CORS!'}));
res.end();
}
}).listen(3000, function() {
console.log('Listening on port 3000');
});
这段代码创建了一个HTTP服务器,当浏览器向该服务器发送CORS请求时,服务器会在响应头中添加
Access-Control-Allow-Origin和
Access-Control-Allow-Credentials字段,从而允许跨域访问。