大家好,我是苏三,又跟大家见面了。
1 什么是跨域问题?
很多小伙伴第一次遇到跨域问题,大概率会一脸懵逼:“我后端接口明明通了,Postman也能调,为啥浏览器就报红字?”
其实这事儿得怪浏览器的**“同源策略”** (Same-Origin Policy)。
简单说,浏览器觉得“不同源的请求都是耍流氓”。
比如你的前端跑在http://localhost:8080。
只要协议
、域名
、端口
任何一个不同,就会被浏览器直接掐断。
举个栗子🌰:
// 前端代码(http://localhost:8080)
fetch('http://api.xxx.com:8000/user')
.then(res => res.json())
.then(data => console.log(data));
// 浏览器控制台报错:
// Access to fetch from 'http://localhost:8080' has been blocked by CORS policy...
这时候,你就需要“跨域解决方案”来帮浏览器松绑了!
那么,如何解决跨域问题呢?
2 解决跨域问题的方案
2.1 CORS(跨域资源共享)
适用场景 :前后端分离项目、接口需要兼容多种客户端。
CORS是W3C标准,后端只需在响应头里加几个字段,告诉浏览器**“这个接口我允许谁访问”** 。
后端代码示例(Spring Boot版) :
// 方法1:直接怼注解(适合单个接口)
@CrossOrigin(origins = "http://localhost:8080")
@GetMapping("/user")
public User getUser() { ... }
// 方法2:全局配置(一劳永逸)
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:8080")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
关键响应头 :
- Access-Control-Allow-Origin: http://localhost:8080(允许的源)
- Access-Control-Allow-Methods: GET,POST(允许的方法)
- Access-Control-Allow-Credentials: true(允许带Cookie)
注意坑点 :
- 如果用了
allowCredentials(true)
,allowedOrigins
不能为*
(必须明确指定域名)。 - 复杂请求(比如Content-Type是
application/json
)会先发一个OPTIONS预检请求 ,记得处理!
2.2 JSONP
适用场景 :老项目兼容、只支持GET请求(比如调用第三方地图API)。
JSONP利用**<script>
标签没有跨域限制**的特性,让后端返回一段JS代码。
前端代码 :
function handleUserData(data) {
console.log("收到数据:", data);
}
// 动态创建script标签
const script = document.createElement('script');
script.src = 'http://api.xxx.com:8000/user?callback=handleUserData';
document.body.appendChild(script);
后端代码 :
@GetMapping("/user")
public String jsonp(@RequestParam String callback) {
User user = new User("Tony", 30);
// 把数据包进回调函数里
return callback + "(" + new Gson().toJson(user) + ")";
}
输出结果 :
handleUserData({"name":"Tony","age":30})
缺点 :
- 只支持GET(传参长度有限)。
- 容易被XSS攻击(毕竟得信任第三方脚本)。
2.3 Nginx反向代理
适用场景 :生产环境部署、微服务网关统一处理。
直接把跨域问题甩给Nginx,让浏览器以为所有请求都是同源 的。
Nginx配置示例 :
server {
listen 80;
server_name localhost;
location /api {
# 转发到真实后端
proxy_pass http://api.xxx.com:8000;
# 解决跨域
add_header 'Access-Control-Allow-Origin' 'http://localhost:8080';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type';
}
}
此时前端请求地址改成同源 :
fetch('/api/user') // 实际访问 http://localhost/api/user → 被Nginx转发
优点 :
- 前后端代码零侵入。
- 能隐藏真实接口地址(安全加分)。
2.4 网关层统一处理
适用场景 :Spring Cloud Gateway、Kong等API网关。
和Nginx思路类似,但更适合微服务场景,直接在网关层加CORS配置。
Spring Cloud Gateway配置 :
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowed-origins: "http://localhost:8080"
allowed-methods: "*"
allowed-headers: "*"
allow-credentials: true
2.5 WebSocket
适用场景 :实时通信需求(聊天室、股票行情)。
WebSocket协议没有跨域限制(因为握手阶段走HTTP,后续升级为长连接)。
前端代码 :
const socket = new WebSocket('ws://api.xxx.com:8000/ws');
socket.onmessage = (event) => {
console.log('收到消息:', event.data);
};
后端代码(Spring Boot) :
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyWebSocketHandler(), "/ws");
}
}
2.6 PostMessage
适用场景 :页面与iframe、弹窗之间的跨域通信。
通过window.postMessage
实现不同窗口间的数据传递。
父页面(http://parent.com) :
const childWindow = window.open('http://child.com');
childWindow.postMessage('我是你爹', 'http://child.com');
子页面(http://child.com) :
window.addEventListener('message', (event) => {
if (event.origin !== 'http://parent.com') return; // 验证来源
console.log('收到爹的消息:', event.data);
});
总结
- 简单粗暴 :开发环境用CORS注解。
- 生产环境 :优先Nginx/网关统一处理,避免每个服务配一遍。
- 老项目兼容 :JSONP勉强能用,但别长期依赖。
- 实时场景 :直接上WebSocket,顺便解决通信问题。
- 安全第一 :
Access-Control-Allow-Origin
尽量别写*
,白名单要用精确域名。
最后提醒温馨提醒一下:跨域问题本质是浏览器行为 ,和HTTP协议无关。
如果你用Postman,发送curl请求,测试没问题,但浏览器报错,别怀疑人生,这可能是前端的锅!
最后欢迎 加入苏三的星球 ,你将获得:苏三AI项目、 商城微服务实战、秒杀系统实战 、 商城系统实战、秒杀系统实战、代码生成工具、系统设计、性能优化、技术选型、底层原理、Spring源码解读、工作经验分享、痛点问题、面试八股文等多个优质专栏。
还有1V1答疑、修改简历、职业规划、送书活动、技术交流。
目前星球已经更新了5200+篇优质内容,还在持续爆肝中.....