第一章:跨域问题的本质与浏览器安全机制
跨域问题源于浏览器实施的同源策略(Same-Origin Policy),这是一种核心的安全机制,旨在隔离不同来源的网页资源,防止恶意脚本读取敏感数据。所谓“同源”,需满足协议、域名和端口三者完全一致;任意一项不同即构成跨域。
同源策略的作用范围
同源策略主要限制以下行为:
- XMLHttpRequest 或 Fetch 发起的跨域 HTTP 请求
- DOM 的跨页面访问(如 iframe 间的脚本调用)
- Cookie、LocalStorage 和 IndexDB 的跨域读取
需要注意的是,该策略由浏览器强制执行,服务器无法直接控制,且仅针对前端发起的请求生效。例如,使用
<img>、
<script> 或
<link> 标签加载跨域资源不受此限制,这也是 JSONP 等历史方案得以实现的基础。
跨域资源共享(CORS)的基本原理
现代 Web 通过 CORS 机制在保障安全的前提下允许可控的跨域访问。当浏览器检测到跨域请求时,会自动附加请求头,并根据请求类型决定是否预先发送预检请求(Preflight Request)。
GET /api/user HTTP/1.1
Host: api.example.com
Origin: https://myapp.com
服务器需响应包含权限声明的头部信息:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
常见跨域场景对比
| 请求类型 | 是否触发预检 | 说明 |
|---|
| 简单 GET 请求 | 否 | 仅含基本头部,如 Accept、Content-Type(限值) |
| 带自定义头部的 POST | 是 | 浏览器先发送 OPTIONS 请求确认权限 |
graph TD
A[前端发起跨域请求] --> B{是否为简单请求?}
B -->|是| C[直接发送请求]
B -->|否| D[先发送OPTIONS预检]
D --> E[服务器返回CORS策略]
E --> F[若允许,则继续原始请求]
第二章:CORS跨域资源共享深度解析
2.1 CORS基本原理与简单请求 vs 预检请求
CORS(跨源资源共享)是浏览器实现的一种安全机制,用于控制跨域请求的资源访问权限。其核心在于服务器通过响应头告知浏览器是否允许当前源发起的请求。
简单请求
满足特定条件的请求无需预检,直接发送。例如使用 GET、POST 方法,且仅包含标准首部(如 Content-Type 值为 application/x-www-form-urlencoded、multipart/form-data 或 text/plain)。
GET /data HTTP/1.1
Host: api.example.com
Origin: https://site-a.com
该请求若符合简单请求规范,浏览器直接发送,服务器需返回
Access-Control-Allow-Origin 头以授权。
预检请求
对于非简单请求(如使用 PUT 方法或自定义头部),浏览器先发送 OPTIONS 请求进行探测:
OPTIONS /data HTTP/1.1
Host: api.example.com
Origin: https://site-a.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
服务器响应后,若确认允许,则浏览器发送实际请求。此机制保障了跨域操作的安全性与可控性。
2.2 服务端配置Access-Control-Allow-Origin实践
在跨域资源共享(CORS)机制中,服务端需正确设置响应头 `Access-Control-Allow-Origin`,以允许指定或所有来源的请求访问资源。
常见配置方式
- 允许单一域名访问:
Access-Control-Allow-Origin: https://example.com - 允许任意来源访问:
Access-Control-Allow-Origin: * - 动态匹配请求源(推荐安全做法)
Node.js Express 示例
app.use((req, res, next) => {
const allowedOrigins = ['https://example.com', 'https://api.example.com'];
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.header('Access-Control-Allow-Origin', origin);
}
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
上述代码通过检查请求头中的
Origin 值,动态设置允许的来源,避免通配符
* 带来的安全风险。同时配置了支持的请求方法和头部字段,确保复杂请求(如携带 token)能正常预检。
2.3 自定义请求头与预检请求的触发条件分析
当浏览器发起跨域请求时,是否触发预检请求(Preflight Request)取决于请求是否属于“简单请求”。若携带自定义请求头,则通常会触发预检。
触发预检的条件
以下情况将导致浏览器先发送
OPTIONS 预检请求:
- 使用了自定义请求头字段,如
X-Auth-Token - Content-Type 的值为
application/json 等非简单类型 - 请求方法为
PUT、DELETE 等非安全方法
代码示例:触发预检的请求
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Request-By': 'user-client' // 自定义头,触发预检
},
body: JSON.stringify({ name: 'test' })
});
上述代码中,
X-Request-By 是非标准头字段,浏览器将其识别为“非简单请求”,从而自动发起
OPTIONS 请求以确认服务器是否允许该跨域操作。服务器需正确响应
Access-Control-Allow-Headers: X-Request-By 才能通过预检。
2.4 凭据传递与withCredentials的正确使用方式
在跨域请求中,携带用户凭据(如 Cookie)需显式启用 `withCredentials` 属性。该配置允许浏览器在跨源请求中发送认证信息,但必须配合服务端 CORS 策略共同设置。
基本用法示例
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/user');
xhr.withCredentials = true; // 关键:开启凭据传递
xhr.send();
上述代码中,
withCredentials = true 指示浏览器在跨域请求中包含凭据(如 Cookie)。若未设置,即使存在有效会话凭证,服务器也无法识别用户身份。
常见配置对照表
| 客户端设置 | 服务端响应头 | 是否生效 |
|---|
| withCredentials: true | Access-Control-Allow-Origin: https://example.com | 否(精确匹配但缺少凭据支持) |
| withCredentials: true | Access-Control-Allow-Origin: https://example.com Access-Control-Allow-Credentials: true | 是 |
注意:当使用 `withCredentials` 时,服务端不可将 `Access-Control-Allow-Origin` 设置为 `*`,必须指定明确的源。
2.5 复杂请求中常见错误码排查与修复策略
在处理跨域、鉴权或大文件上传等复杂请求时,HTTP 错误码频繁出现,需系统性排查。
典型错误码及成因
- 401 Unauthorized:认证信息缺失或过期
- 403 Forbidden:权限不足,服务端拒绝访问
- 413 Payload Too Large:请求体超出服务器限制
- 502 Bad Gateway:网关后端服务异常或超时
预检请求失败修复示例
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
res.sendStatus(200); // 预检请求正常响应
} else {
next();
}
});
上述代码确保了 CORS 预检(OPTIONS)请求返回 200 而非 404 或 500,避免触发浏览器的
ERR_FAILED 错误。关键在于拦截 OPTIONS 方法并提前结束响应。
服务器配置调优建议
调整 Nginx 等反向代理参数可解决 413 错误:
| 指令 | 推荐值 | 说明 |
|---|
| client_max_body_size | 100M | 允许最大请求体大小 |
| proxy_read_timeout | 300s | 防止慢速后端导致 504 |
第三章:JSONP跨域方案的原理与应用
3.1 JSONP实现跨域的底层机制与历史背景
在早期Web开发中,浏览器的同源策略严格限制了不同源之间的资源请求。为解决前端跨域获取数据的问题,开发者巧妙利用了