跨域问题
问题表现
前端请求后端接口时出现以下错误:
Access to XMLHttpRequest at 'http://localhost:8080/api/v1/users'
from origin 'http://localhost:5173' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.原因分析
跨域是浏览器的同源策略限制:
| 对比项 | 前端 | 后端 | 是否同源 |
|---|---|---|---|
| 协议 | http | http | ✅ |
| 域名 | localhost | localhost | ✅ |
| 端口 | 5173 | 8080 | ❌ |
端口不同,触发跨域限制。
解决方案
方案 1:后端配置 CORS(推荐)
Java Spring Boot
java
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// 允许的域名
config.addAllowedOriginPattern("*");
// 允许的请求头
config.addAllowedHeader("*");
// 允许的方法
config.addAllowedMethod("*");
// 允许携带凭证(Cookie)
config.setAllowCredentials(true);
// 暴露的响应头
config.addExposedHeader("Authorization");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}Node NestJS
typescript
// main.ts
app.enableCors({
origin: ['http://localhost:5173'],
credentials: true,
allowedHeaders: ['Content-Type', 'Authorization'],
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']
})Go Gin
go
func CorsMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "http://localhost:5173")
c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type,Authorization")
c.Header("Access-Control-Allow-Credentials", "true")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}方案 2:Vite 代理(开发环境)
typescript
// vite.config.ts
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '/api')
}
}
}
})前端请求 /api/v1/users → 代理到 http://localhost:8080/api/v1/users
优点:无需后端配置,适合开发环境
缺点:生产环境不适用
方案 3:Nginx 反向代理(生产环境)
nginx
server {
listen 80;
server_name vue.youlai.tech;
# 前端
location / {
root /var/www/html;
try_files $uri $uri/ /index.html;
}
# 后端 API
location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# CORS 头
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET,POST,PUT,DELETE,OPTIONS';
add_header Access-Control-Allow-Headers 'Content-Type,Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
}
}方案 4:JSONP(不推荐)
只支持 GET 请求,不适用于现代 Web 应用。
常见问题
1. Cookie 无法携带
原因:跨域请求默认不携带 Cookie
解决:
前端:
typescript
axios.defaults.withCredentials = true后端:
java
config.setAllowCredentials(true)
config.addAllowedOrigin("http://localhost:5173") // 不能用 *2. OPTIONS 预检请求失败
原因:浏览器先发送 OPTIONS 请求检查是否允许跨域
解决:
java
if (request.getMethod().equals("OPTIONS")) {
response.setStatus(204)
return
}3. SSE 跨域问题
问题:EventSource 不支持自定义请求头
解决:
typescript
// 使用查询参数传递 Token
const eventSource = new EventSource(
`/api/v1/sse/connect?accessToken=${token}`
)后端:
java
@GetMapping("/sse/connect")
public void sseConnect(@RequestParam String accessToken,
HttpServletResponse response) {
// 验证 Token
// ...
response.setHeader("Access-Control-Allow-Origin", "*")
}4. WebSocket 跨域
typescript
const ws = new WebSocket('ws://localhost:8080/ws')后端需要配置 WebSocket CORS:
java
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/ws")
.setAllowedOrigins("*");
}
}调试技巧
Chrome DevTools
查看 Network 标签:
Response Headers:
Access-Control-Allow-Origin: http://localhost:5173
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, DELETEcurl 测试
bash
curl -X OPTIONS http://localhost:8080/api/v1/users \
-H "Origin: http://localhost:5173" \
-H "Access-Control-Request-Method: GET" \
-v最佳实践
| 环境 | 推荐方案 |
|---|---|
| 开发环境 | Vite 代理 |
| 测试环境 | 后端 CORS |
| 生产环境 | Nginx 反向代理 |
