同源策略,解决跨域的方法
跨域是由于浏览器的同源策略导致的,即不同域名、协议或端口之间的网页脚本不能相互访问。为了解决跨域问题,常见的方法包括:
- JSONP(只支持 GET 请求):JSONP 是一种利用 <script> 标签可以跨域加载资源的特性来实现跨域请求的方法。它的原理是在客户端动态创建一个 <script> 标签,标签的 src 属性指向服务器的 API 地址,并且传递一个回调函数名作为参数,服务器返回的数据将会作为参数传入回调函数中。由于 <script> 标签可以跨域加载资源,因此可以获取到服务器返回的数据。代码示例:
<!-- 客户端 HTML 页面 -->
<script>
function handleResponse(data) {
console.log(data); // 处理服务器返回的数据
}
</script>
<script src="http://api.example.com/data?callback=handleResponse"></script>
// 服务器返回的数据
handleResponse({"name": "John", "age": 30});
封装一个简单的 JSONP 请求函数可以使用 Promise 来处理异步操作,并动态创建一个 <script> 标签来实现跨域请求。以下是一个简单的 JSONP 封装示例:
function jsonp(url, params) {
// 生成一个唯一的回调函数名
const callbackName = `jsonp_callback_${Date.now()}`;
// 将参数拼接到 URL 中
const queryParams = new URLSearchParams(params);
url += `${url.includes('?') ? '&' : '?'}${queryParams.toString()}`;
return new Promise((resolve, reject) => {
// 创建回调函数
window[callbackName] = function (data) {
resolve(data);
// 请求完成后删除回调函数和 <script> 标签
delete window[callbackName];
script.parentNode.removeChild(script);
};
// 创建 <script> 标签,并设置其 src 属性
const script = document.createElement('script');
script.src = `${url}&callback=${callbackName}`;
// 加载失败处理
script.onerror = () => {
reject(new Error('JSONP request failed'));
delete window[callbackName];
script.parentNode.removeChild(script);
};
// 将 <script> 标签添加到文档中,触发请求
document.body.appendChild(script);
});
}
// 使用示例:
// 请求 JSONP 数据
jsonp('http://api.example.com/data', { key: 'value' })
.then(data => {
console.log(data); // 处理服务器返回的数据
})
.catch(error => {
console.error(error);
});
- CORS(Cross-Origin Resource Sharing):CORS 是一种基于 HTTP 头部的机制,允许服务器声明哪些域名可以访问它的资源。在客户端发送请求时,浏览器会自动附加 Origin 头部,并且在响应中会包含 Access-Control-Allow-Origin 头部,用于指示是否允许特定的域名访问资源。代码示例(使用 Express 框架):
// 服务器端代码
const express = require('express');
const app = express();
// 设置允许跨域访问的域名(注意:如果使用 *,表示允许任意域名访问)
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', 'http://example.com');
next();
});
// 处理跨域请求
app.get('/data', (req, res) => {
const data = { name: 'John', age: 30 };
res.json(data);
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
<!-- 客户端 HTML 页面 -->
<script>
fetch('http://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data)); // 处理服务器返回的数据
</script>
- 代理服务器:使用代理服务器是另一种解决跨域问题的方法。在客户端发送请求时,将请求发送给自己的服务器,然后由服务器去请求目标服务器的资源,最后将目标服务器的响应返回给客户端。由于请求是发送给同源的服务器,因此不存在跨域问题。代码示例(使用 Express 框架):
// 服务器端代码
const express = require('express');
const axios = require('axios');
const app = express();
// 处理跨域请求
app.get('/data', async (req, res) => {
try {
const response = await axios.get('http://api.example.com/data');
res.json(response.data);
} catch (error) {
res.status(500).json({ error: 'An error occurred' });
}
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
<!-- 客户端 HTML 页面 -->
<script>
fetch('http://localhost:3000/data')
.then(response => response.json())
.then(data => console.log(data)); // 处理服务器返回的数据
</script>
- WebSocket是一种全双工通信协议,它建立在TCP上,可以在浏览器和服务器之间创建持久连接,实现实时的双向数据传输。WebSocket不受同源策略的限制,可以在任意域名下建立连接。
//在浏览器端使用JavaScript代码创建WebSocket对象,并指定要连接的服务器地址:
const socket = new WebSocket('ws://example.com'); // 注意使用ws协议,而不是http或https
//通过WebSocket对象的事件处理函数来处理连接和消息:
socket.onopen = () => {
console.log('WebSocket连接已打开');
};
socket.onmessage = event => {
console.log('收到消息:', event.data);
};
socket.onclose = () => {
console.log('WebSocket连接已关闭');
};
socket.onerror = error => {
console.error('WebSocket发生错误:', error);
};
//通过WebSocket对象的方法发送消息到服务器:
socket.send('Hello, server!');
//服务器端也需要实现WebSocket协议来处理客户端的连接和消息。
- postMessage是HTML5中提供的一种跨文档通信的方法,它允许一个窗口与另一个窗口进行安全的跨域通信。使用postMessage,可以在一个页面中发送消息到另一个页面,无论这两个页面是否同源。在postMessage中,第一个参数是要发送的消息,第二个参数是目标窗口的origin,用来限制只接收来自特定域的消息,防止恶意代码的攻击。
- 在发送消息的页面中,使用postMessage方法向目标窗口发送消息:
// 发送消息到目标窗口
window.postMessage('Hello from Page A!', 'https://example.com');
- 在目标页面中,监听message事件来接收消息:
// 监听message事件来接收消息
window.addEventListener('message', event => {
// 判断消息来源是否可信,防止恶意代码
if (event.origin === 'https://example.com') {
console.log('收到消息:', event.data);
// 可以在这里处理接收到的消息
}
});
这些方法可以根据具体的情况来选择使用,以解决不同场景下的跨域问题。

