HTTP的跨域问题
什么是跨域
跨域,英文:Cross-Origin Resource Sharing, CORS。是指一个域名的网页去请求另一个域名的资源时遇到的安全限制问题,因为浏览器出于安全考虑,实施了同源策略 (Same-Origin Policy),这意味着在默认情况下,一个域下的网页不能直接访问另一个域的资源。
什么是同源策略
- 同源策略是一种安全协议,源 (Recourse) 是同时由域名(如example.com)、协议(如http, https)、端口号同时决定的。
- 同源策略规定了来自不同的源的网页不能直接读取或者修改彼此的文档内容、Cookie、LocalStorage等等,也不能通过AJAX(Asynchronous JavaScript and XML)直接请求不同源的数据
跨域带来的问题
当一个网页尝试请求不同源的API、图片、脚本等资源的时候就会遇到跨域问题。例如一个运行在https://www.example.com的网页尝试通过AJAX请求https://api.another.com的数据,很明显这里域名不同,这样就会被浏览器限制。
跨域常见的解决方法
- CORS(跨源资源共享):
- 服务端设置HTTP响应头:在服务端设置
Access-Control-Allow-Origin头部。例如,服务器可以设置Access-Control-Allow-Origin: https://www.exmaple.com来允许特定源的请求,或者Access-Control-Allow-Origin: *允许所有源的请求。 - 浏览器会在发送跨域请求时进行CORS预检请求来确保安全。
- CORS是一种更安全且更强大的跨域通信方法,建议使用CORS代替JSONP。 下面是Express中使用CORS中间件来简化CORS的配置
const express = require('express');
const cors = require('cors');
const app = express();
// CORS配置
const corsOptions = {
origin: 'https://example.com', // 允许访问的源
methods: 'GET,POST,PUT,DELETE', // 允许的HTTP方法
allowedHeaders: 'Content-Type,Authorization' // 允许的HTTP头部
};
// 使用CORS中间件
app.use(cors(corsOptions));
// 示例路由
app.get('/data', (req, res) => {
res.json({ message: '这是跨域可访问的数据' });
});
app.listen(8080, () => {
console.log('Server running on port 8080');
});
- JSONP (JSON with Padding):
- HTML中的
script标签src属性的链接可以访问跨域的JS脚本,不受浏览器的同源策略影响。 - JSONP只能够支持
GET请求,携带数据较小。
// 1. 创建一个动态的script标签
const script = document.createElement("script")
// 2. 设置回调函数
function jsonpCallback(data) {
// ...
}
// 3. script的src属性设置为接口地址
script.src = "https://example.com/api/data?callback=jsonpCallback"// URL包含回调函数的名称
// 4. 让script生效
document.body.appendChild(script)
- 我们将请求发送到
https://exmaple.com/api/data - 查询参数设置为
callback=jsonpCallback告诉服务端我们的回调函数是jsonpCallback。
- 代理服务器
- 在服务器端创建一个代理,前端与代理通信,代理再向目标服务器发送请求,并且将结果返回给前端。
- 使用PostMessage:
postMessage是HTML5中引入的一种安全的跨域通信方式。这个方法允许一个窗口向另一个窗口发送消息,无论这两个窗口是否同源。- 假设我们有两个窗口:窗口A(发送方)和窗口B(接收方)。窗口B可能是一个
iframe,一个弹出窗口,或者是一个新的标签页 - 窗口B(接收方)的代码:
<!DOCTYPE html>
<html>
<head>
<title>窗口B</title>
</head>
<body>
<script>
window.addEventListener('message', (event) => {
// 安全检查:验证消息来源
if (event.origin !== "http://example.com") {
return; // 来自未知源的消息不处理
}
console.log(event.data);
// ...
});
</script>
</body>
</html>
这段代码中,event.origin 包含了发送消息的窗口的源(协议 + 域名 + 端口号)。出于安全考虑,你应该检查这个值,以确保消息来自于可信的源。
- 窗口A(发送方)的代码
<!DOCTYPE html>
<html>
<head>
<title>窗口A</title>
</head>
<body>
<script>
// 假设你已经有了窗口B的引用,比如一个iframe
const windowB = document.getElementById('myIframe').contentWindow;
// 向窗口B发送消息
windowB.postMessage("Hello, world!", 'http://example.com');
</script>
<!-- 假设窗口B是一个iframe -->
<iframe id="myIframe" src="http://example.com/windowB.html"></iframe>
</body>
</html>