跨域与CORS#
一句话答案#
同源策略是浏览器的安全基石(协议+域名+端口三者全同才算同源);跨域被拦是浏览器拦响应而非请求没发出;CORS 通过服务端响应头授权跨域,复杂请求会先发 OPTIONS 预检。
核心要点
同源 = 协议 + 域名 + 端口 完全相同,任一不同即跨域:
| 对比 URL | 是否同源 | 原因 |
|---|---|---|
http://a.com/x vs http://a.com/y | 同源 | 路径不影响 |
http://a.com vs https://a.com | 跨域 | 协议不同 |
http://a.com vs http://b.com | 跨域 | 域名不同 |
http://a.com:80 vs http://a.com:8080 | 跨域 | 端口不同 |
关键认知: 跨域请求其实已经发到服务端并执行了,是浏览器根据响应头判断未授权后拦截了响应不给 JS 读取。所以 CORS 是浏览器行为,Postman/服务端之间调用不存在跨域。
简单请求 vs 预检请求(CORS 核心区分):
满足以下全部条件 = 简单请求,直接发,不预检:
- 方法为 GET / POST / HEAD
- Content-Type 只能是
text/plain、application/x-www-form-urlencoded、multipart/form-data - 无自定义请求头
否则为复杂请求(如 application/json、带 Authorization 自定义头、PUT/DELETE),浏览器先自动发一个 OPTIONS 预检请求问服务端是否允许,通过后才发真实请求。
关键 CORS 响应头:
| 响应头 | 作用 |
|---|---|
Access-Control-Allow-Origin | 允许的源(* 或具体域名,带凭证时不能用 *) |
Access-Control-Allow-Methods | 允许的方法(预检响应) |
Access-Control-Allow-Headers | 允许的自定义头(预检响应) |
Access-Control-Allow-Credentials | 是否允许携带 Cookie(true 时 Origin 必须具体) |
Access-Control-Max-Age | 预检结果缓存秒数,减少 OPTIONS 次数 |
面试回答(2分钟版)
跨域问题源于浏览器的同源策略,它要求协议、域名、端口三者完全相同才算同源,否则 JS 发起的请求就是跨域。同源策略是浏览器的安全机制,防止恶意网站读取其他站点的数据。一个常见误区是以为跨域请求没发出去,其实请求已经到达服务端并执行了,只是浏览器拿到响应后发现没有授权头,就把响应拦截了不给 JS 读取,所以服务端到服务端的调用、Postman 调接口都不存在跨域。解决跨域最规范的方式是 CORS,由服务端通过响应头来授权。CORS 把请求分为简单请求和复杂请求:简单请求是 GET、POST、HEAD 且 Content-Type 受限、无自定义头,浏览器直接发;复杂请求比如 application/json 的 POST、带 Authorization 头的请求、PUT 和 DELETE,浏览器会先自动发一个 OPTIONS 预检请求询问服务端,服务端在响应里通过 Access-Control-Allow-Origin、Allow-Methods、Allow-Headers 告知允许范围,通过后才发真实请求,预检结果还能用 Max-Age 缓存减少开销。如果要携带 Cookie,前端要设置 withCredentials,服务端 Allow-Credentials 设为 true,且 Allow-Origin 不能是星号必须是具体域名。除了 CORS,生产中更常用 Nginx 反向代理或 API 网关把前端和接口统一到同一个域名下,从根本上绕过跨域;老项目还有 JSONP,但它只支持 GET 且有安全风险,现在基本淘汰了。
追问与易错
追问方向:
- “为什么有了同源策略还能跨域提交表单(CSRF)?”→ 同源策略限制的是 JS 读取响应,但 form 表单提交、img/script 标签加载这类请求本身不受限制照样能发出去并带上 Cookie,所以才有 CSRF 风险,需要 CSRF Token + SameSite 防御,见 XSS与CSRF防御
- “预检请求会缓存吗?”→ 会,服务端用
Access-Control-Max-Age指定缓存秒数,缓存期内同样的复杂请求不再重复发 OPTIONS,Chrome 上限约 2 小时 - “带 Cookie 跨域要注意什么?”→ 前端
xhr.withCredentials=true或 fetchcredentials:'include',服务端Allow-Credentials:true,且Allow-Origin必须是具体域名不能用*,否则浏览器拒绝;此外 Cookie 的 SameSite 属性也要放开 - “CORS 和 JSONP 区别?”→ CORS 支持所有 HTTP 方法、能处理错误、是标准方案;JSONP 利用 script 标签不受同源限制的特性,只支持 GET、无法处理错误、依赖服务端配合返回函数包裹,有 XSS 风险,已淘汰
- “Nginx 怎么解决跨域?”→ 两种思路:一是反向代理让前端和接口同域(推荐,根本上无跨域);二是在 Nginx 配置
add_header Access-Control-Allow-Origin等响应头由网关统一加 CORS
易错点:
- ❌ 跨域请求被浏览器拦在发送前——实际请求已发送并执行,拦的是响应
- ❌ 同源策略能防 CSRF——它只防读取响应,不防请求发出,CSRF 照样能打
- ❌ 带凭证时
Allow-Origin可以用*——必须是具体域名 - ❌ 跨域是服务端报错——跨域报错出现在浏览器控制台,服务端通常正常返回 200