【浏览器】浏览器跨域

浏览器跨域

相关文章

浏览器跨域


一、跨域资源共享(CORS)

CORS 是 W3C 标准,通过在响应头中设置 Access-Control-Allow-Origin 等字段,允许服务器端指定允许的来源域名,从而允许浏览器跨域访问资源。服务器可以设置允许访问的来源、允许的 HTTP 方法、允许的请求头等。

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const express = require('express');
const app = express();
const PORT = 3000;

// 设置 CORS 头信息
app.use((req, res, next) => {
// 允许所有域名跨域访问
res.setHeader('Access-Control-Allow-Origin', '*');
// 允许跨域请求的方法
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
// 允许跨域请求的请求头
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});

// 路由示例
app.get('/data', (req, res) => {
// 返回 JSON 数据
res.json({ message: 'Hello from API server!' });
});

// 启动服务器
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});

或者Nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server {
listen 80;
server_name example.com;

location / {
# 允许跨域访问的域名,* 表示允许任意域名访问,可以根据需要设置具体的域名
add_header Access-Control-Allow-Origin *;

# 允许的请求方法,可以根据需要添加其他请求方法
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";

# 允许的请求头,可以根据需要添加其他请求头
add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept";

# 允许跨域请求时是否发送身份凭证(cookies、HTTP认证及客户端SSL证明等)
add_header Access-Control-Allow-Credentials "true";

# OPTIONS 预检请求的缓存时间(单位秒),可以根据需要设置
add_header Access-Control-Max-Age 3600;
}
}

客户端

1
2
3
4
5
6
7
8
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://api.example.com/data', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
console.log(xhr.responseText);
}
};
xhr.send();

服务端和NG设置的区别

  • 服务端设置

    • 在服务端代码中,例如使用 Node.js、Java、Python 等语言编写的应用程序,可以通过在 HTTP 响应头中设置 Access-Control-Allow-Origin 来允许跨域请求。这样可以确保服务端在接收到跨域请求时能够正确处理,并返回允许跨域的头信息。
    • 服务端设置的 Access-Control-Allow-Origin 是针对特定的 API 或资源有效的,因为它是在处理请求时由服务端动态设置的,可以根据具体的需求来配置。
    • 服务端设置可以灵活地根据请求的来源和需要返回不同的跨域策略,因为服务端有完整的请求信息和处理逻辑。
  • NG 设置

    • 在 NG 配置中,可以通过在 NG 的配置文件中设置 add_header 指令来添加 Access-Control-Allow-Origin 头信息。这样可以在 NG 层面上对所有请求统一设置跨域头信息,而不需要在每个服务端应用程序中单独设置。
    • NG 设置的 Access-Control-Allow-Origin 是针对 NG 服务器上的所有资源或特定路径的资源有效的。因此,它会对 NG 服务器上托管的所有资源生效,包括静态文件、API 等。
    • NG 设置可以方便地统一管理和配置跨域头信息,特别是在有大量资源需要处理的情况下,可以减少重复工作,并确保所有资源都遵循相同的跨域策略。

二、JSONP(JSON with Padding)

JSONP 是一种利用 <script> 标签的跨域请求方式,通过动态创建 <script> 标签,将请求放到一个指定的回调函数中,从而实现跨域数据传输。JSONP 只支持 GET 请求,且存在安全风险(可能受到 XSS 攻击),因此在使用时需要谨慎。

服务端

1
2
3
4
5
6
7
// api.example.com
app.get('/getData', (req, res) => {
const data = { message: 'Hello from API server!' };
const callback = req.query.callback;
const response = `${callback}(${JSON.stringify(data)})`;
res.send(response);
});

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JSONP Example</title>
</head>
<body>
<script>
function processData(data) {
console.log('Data from API server:', data);
}
</script>
<script src="http://api.example.com/getData?callback=processData"></script>
</body>
</html>

三、代理(Proxy)

在同源策略下,浏览器允许向同源服务器发出请求,因此可以在同源服务器上搭建代理服务,将跨域请求发送到目标服务器,并将响应返回给客户端,从而避免了浏览器的跨域限制。

代理服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// proxy-server.js

const express = require('express');
const httpProxy = require('http-proxy');

const app = express();
const proxy = httpProxy.createProxyServer();

const API_SERVER = 'http://api.example.com';

// 设置代理路由
app.all('/api/*', (req, res) => {
proxy.web(req, res, { target: API_SERVER });
});

// 启动代理服务器
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Proxy server is running on http://localhost:${PORT}`);
});

客户端

1
2
3
4
5
// Frontend code in example.com
fetch('/api/data')
.then(response => response.json())
.then(data => console.log('Data from API server:', data))
.catch(error => console.error('Error:', error));

四、跨文档消息传递(Cross-document Messaging)

使用 postMessage 方法可以在不同窗口或 iframe 之间进行跨域通信,可以实现双向数据传递。

客户端1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Parent Page</title>
</head>
<body>
<h1>Parent Page</h1>
<iframe id="myFrame" src="http://api.example.com/iframe.html" width="400" height="300"></iframe>

<script>
const iframe = document.getElementById('myFrame').contentWindow;

// 向 iframe 发送消息
iframe.postMessage('Hello from Parent Page!', 'http://api.example.com');
</script>
</body>
</html>

客户端2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Child Iframe</title>
</head>
<body>
<h1>Child Iframe</h1>

<script>
// 监听来自父页面的消息
window.addEventListener('message', event => {
// 确保消息来自指定的父页面
if (event.origin === 'http://example.com') {
console.log('Message from Parent Page:', event.data);
}
});
</script>
</body>
</html>

五、WebSocket

WebSocket 是一种全双工通信协议,可以在浏览器和服务器之间建立持久连接,允许双方进行双向通信,不受同源策略的限制。

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// websocket-server.js

const express = require('express');
const http = require('http');
const WebSocket = require('ws');

const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });

// 当有新的 WebSocket 连接建立时
wss.on('connection', (ws) => {
console.log('New WebSocket connection established');

// 当收到客户端发送的消息时
ws.on('message', (message) => {
console.log('Received message from client:', message);

// 向客户端发送消息
ws.send('Hello from WebSocket server!');
});
});

// 启动服务器
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log(`WebSocket server is running on http://localhost:${PORT}`);
});

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!-- websocket-client.html -->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebSocket Client</title>
</head>
<body>
<h1>WebSocket Client</h1>

<script>
// 创建 WebSocket 连接
const ws = new WebSocket('ws://localhost:3000');

// 当连接建立时
ws.onopen = () => {
console.log('WebSocket connection established');

// 发送消息到服务器
ws.send('Hello from WebSocket client!');
};

// 当收到服务器发送的消息时
ws.onmessage = (event) => {
console.log('Received message from server:', event.data);
};
</script>
</body>
</html>

六、跨域资源嵌入(Cross-Origin Resource Embedding)

通过将资源(如字体、图片、音视频等)嵌入到 HTML 或 CSS 中,避免了跨域请求,从而避免了跨域问题。

html

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cross-Origin Resource Embedding Example</title>
<link rel="stylesheet" href="http://examplecdn.com/embedded.css">
</head>
<body>
<h1>Cross-Origin Resource Embedding Example</h1>
<div class="image"></div>
</body>
</html>

css

1
2
3
4
5
6
7
/* embedded.css */

.image {
background-image: url('http://assets.examplecdn.com/image.jpg');
width: 100px;
height: 100px;
}

喜欢这篇文章?打赏一下支持一下作者吧!
【浏览器】浏览器跨域
https://www.cccccl.com/20210402/浏览器/浏览器跨域/
作者
Jeffrey
发布于
2021年4月2日
许可协议