⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 mp.weixin.qq.com/s?__biz=MzU4MDUyMDQyNQ==&mid=2247569020&idx=2&sn=6eae1f4a9e8db0a9d0f8b14e15c9b0d5&poc_token=HBZYv2mj-gTC6LBvwa-omD9Aw0eMAvk7Y1e6GdhY 「null」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

跨域是前端面试的常客,但很多人只会说”配个 CORS 就行”。面试官真正想考的是:你是否理解浏览器为什么要限制跨域,以及不同方案各自适用什么场景。

一句话:跨域是浏览器同源策略导致的——协议、域名、端口三者有一个不同,AJAX 请求的响应就会被浏览器拦截。注意,请求是发出去了的,后端也处理了,只是浏览器不让你读响应。

方案 原理 优点 缺点 适用场景
CORS 服务端设响应头 标准方案,功能完整 需后端配合 生产首选
代理服务器 同源代理转发 不改后端 多一层转发 开发环境
JSONP <script> 标签不受限 兼容性好 只支持 GET,不安全 老项目
postMessage 跨窗口通信 安全可控 仅限窗口间 iframe
WebSocket 不受同源限制 全双工 需长连接 实时通信

先搞清楚:什么是同源策略?

同源 = 协议 + 域名 + 端口完全一致。不同源时,浏览器限制:

  • AJAX 请求的响应被拦截(核心限制)
  • Cookie / LocalStorage / IndexedDB 不可跨域读取
  • DOM 不可跨域操作(iframe 场景)

设计初衷很简单:防止恶意网站通过 AJAX 偷读你的银行页面数据。

方案一:CORS(生产首选)

CORS(Cross-Origin Resource Sharing)是 W3C 标准,服务端通过响应头告诉浏览器”这个源我允许”。

简单请求 vs 预检请求

  • 简单请求:GET/POST/HEAD + 常规 Header,浏览器直接发,检查响应头的 Access-Control-Allow-Origin
  • 预检请求:其他情况(PUT/DELETE、自定义 Header、Content-Type: application/json),浏览器先发 OPTIONS 问一嘴,通过了再发真正的请求

简单请求流程

预检请求流程

Spring Boot 配置

// 全局配置(推荐)
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600); // 预检结果缓存 1 小时
}
}

关键响应头

响应头 说明
Access-Control-Allow-Origin 允许的源(* 或具体域名)
Access-Control-Allow-Methods 允许的 HTTP 方法
Access-Control-Allow-Headers 允许的请求头
Access-Control-Allow-Credentials 是否允许带 Cookie
Access-Control-Max-Age 预检结果缓存秒数
// 前端
fetch(url, { credentials: 'include' });
// 或 axios
axios.defaults.withCredentials = true;

// 后端:Allow-Origin 不能为 *,必须指定具体域名
response.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");
response.setHeader("Access-Control-Allow-Credentials", "true");

Cookie 还需设置 SameSite=None; Secure(仅 HTTPS)。

方案二:代理服务器(开发环境)

原理:同源策略只限制浏览器,不限制服务器之间的通信。让前端开发服务器代理转发请求即可。

Vite 配置:

// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
}

Nginx 反向代理(生产环境也能用):

location /api/ {
proxy_pass http://backend-server:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}

方案三:JSONP(老项目兼容)

利用 <script> 标签的 src 不受同源限制,约定回调函数名,后端返回 JS 代码调用它。

function jsonp(url, callback) {
const name = 'jsonp_' + Date.now();
window[name] = (data) => {
callback(data);
delete window[name];
document.body.removeChild(script);
};
const script = document.createElement('script');
script.src = `${url}?callback=${name}`;
document.body.appendChild(script);
}

硬伤:只支持 GET、无法判断 HTTP 状态码、有 XSS 风险。现代项目别用。

方案对比

选型建议:生产环境 CORS + Nginx 反向代理;开发环境 Vite/Webpack 代理;有 iframe 通信需求用 postMessage。

面试高频追问

Q1:表单提交和图片加载为什么不受跨域限制?

跨域限制的目的是防止读取数据。表单提交会跳转页面,原页面拿不到响应;图片只能显示不能读取像素。而 AJAX 能读取响应内容,所以要限制。

Q2:预检请求怎么优化?

设置 Access-Control-Max-Age 缓存预检结果(比如 3600 秒),避免每次都发 OPTIONS。服务端对 OPTIONS 快速返回,不走业务逻辑。

Q3:WebSocket 为什么不受跨域限制?

WebSocket 用 ws:// 协议,安全模型和 AJAX 不同——连接时发送 Origin 头,但由服务端校验而不是浏览器拦截。服务端应该验证 Origin 白名单。

文章目录
  1. 1. 先搞清楚:什么是同源策略?
  2. 2. 方案一:CORS(生产首选)
    1. 2.0.1. 简单请求 vs 预检请求
    2. 2.0.2. Spring Boot 配置
    3. 2.0.3. 关键响应头
    4. 2.0.4. 携带 Cookie 的坑
  • 3. 方案二:代理服务器(开发环境)
  • 4. 方案三:JSONP(老项目兼容)
  • 5. 方案对比
  • 6. 面试高频追问