前端跨域问题及解决方案

1. 为什么会出现跨域问题?

跨域(Cross-Origin)是指浏览器不能执行其他网站的脚本。它是由浏览器的 同源策略 引起的。同源策略是浏览器的一种安全机制,用于防止恶意网站窃取数据。

1.1 同源策略

同源策略要求以下三个部分必须相同:

  • 协议(Protocol):如 httphttps
  • 域名(Domain):如 wyxup.top
  • 端口(Port):如 80443

如果三者中有任何一个不同,就会触发跨域问题。

1.2 跨域示例

以下是一些跨域的示例:

  • http://wyxup.tophttps://wyxup.top(协议不同)。
  • http://wyxup.tophttp://api.wyxup.top(域名不同)。
  • http://wyxup.tophttp://wyxup.top:8080(端口不同)。

2. 跨域问题的表现

当浏览器检测到跨域请求时,会阻止请求并抛出错误。常见的错误信息包括:

  • CORS 错误Access to XMLHttpRequest at 'http://wyxup.top' from origin 'http://localhost:3000' has been blocked by CORS policy
  • JSONP 错误Uncaught SyntaxError: Unexpected token <

3. 跨域解决方案

以下是常见的跨域解决方案:

3.1 CORS(跨域资源共享)

CORS(Cross-Origin Resource Sharing)是 W3C 标准,允许服务器声明哪些源可以访问资源。

3.1.1 服务器端配置

在服务器端设置响应头,允许特定源的请求:

1
Access-Control-Allow-Origin: http://wyxup.top

或者允许所有源的请求:

1
Access-Control-Allow-Origin: *

3.1.2 预检请求

对于复杂请求(如 PUTDELETE),浏览器会先发送一个 OPTIONS 预检请求,服务器需要响应以下头:

1
2
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization

3.2 JSONP(JSON with Padding)

JSONP 是一种利用 <script> 标签不受同源策略限制的特性实现的跨域方案。

3.2.1 实现原理

  • 前端动态创建一个 <script> 标签,src 指向跨域接口,并传递一个回调函数名。
  • 服务器返回一段 JavaScript 代码,调用前端定义的回调函数。

3.2.2 示例代码

前端代码:

1
2
3
4
5
6
7
function handleResponse(data) {
console.log(data);
}

const script = document.createElement('script');
script.src = 'http://wyxup.top/api?callback=handleResponse';
document.body.appendChild(script);

服务器端代码(Node.js):

1
2
3
4
5
6
7
const http = require('http');

http.createServer((req, res) => {
const callback = req.url.split('callback=')[1];
const data = JSON.stringify({ message: 'Hello, JSONP!' });
res.end(`${callback}(${data})`);
}).listen(3000);

3.2.3 局限性

  • 仅支持 GET 请求。
  • 安全性较低,容易受到 XSS 攻击。

3.3 代理服务器

通过代理服务器转发请求,避免浏览器直接访问跨域接口。

3.3.1 实现原理

  • 前端请求同源服务器,同源服务器转发请求到目标服务器。
  • 目标服务器返回数据,同源服务器再将数据返回给前端。

3.3.2 示例代码

使用 Node.js 实现代理服务器:

1
2
3
4
5
6
7
8
const http = require('http');
const httpProxy = require('http-proxy');

const proxy = httpProxy.createProxyServer({});

http.createServer((req, res) => {
proxy.web(req, res, { target: 'http://wyxup.top' });
}).listen(3000);

前端代码:

1
2
3
fetch('http://localhost:3000/api')
.then(response => response.json())
.then(data => console.log(data));

3.4 Nginx 反向代理

通过 Nginx 配置反向代理,将跨域请求转发到目标服务器。

3.4.1 配置示例

在 Nginx 配置文件中添加以下内容:

1
2
3
4
5
6
7
8
9
server {
listen 80;
server_name localhost;

location /api {
proxy_pass http://wyxup.top;
add_header Access-Control-Allow-Origin *;
}
}

3.4.2 前端代码

前端直接请求 Nginx 服务器:

1
2
3
fetch('http://localhost/api')
.then(response => response.json())
.then(data => console.log(data));

3.5 WebSocket

WebSocket 是一种全双工通信协议,不受同源策略限制。

3.5.1 示例代码

前端代码:

1
2
3
4
5
6
7
const socket = new WebSocket('ws://wyxup.top');

socket.onmessage = event => {
console.log(event.data);
};

socket.send('Hello, WebSocket!');

服务器端代码(Node.js):

1
2
3
4
5
6
7
8
9
10
const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', ws => {
ws.on('message', message => {
console.log(`收到消息: ${message}`);
ws.send('Hello, Client!');
});
});