相关文章
浏览器跨域
一、跨域资源共享(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;
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) => { 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"; add_header Access-Control-Allow-Credentials "true"; 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
| 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
|
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
| 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.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
|
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 });
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
|
<!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> 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
|
.image { background-image: url('http://assets.examplecdn.com/image.jpg'); width: 100px; height: 100px; }
|