第一次遇到CORS错误时,我和大多数开发者一样感到困惑。明明后端接口已经正常返回了数据,为什么浏览器还是报错?这个问题得从浏览器的安全基石——同源策略说起。
同源策略就像小区的门禁系统。假设你住在A小区,想要进入B小区的健身房。门卫会检查你的身份:是否属于本小区业主?是否有访客预约?这种检查虽然麻烦,但能防止陌生人随意进出。浏览器对跨域请求的限制也是类似的逻辑,它要确认"这个请求是否来自可信的源"。
具体来说,同源策略要求比较三个要素:
我曾在项目中踩过这样的坑:前端页面用https运行,但接口地址配置成了http。虽然开发环境能正常工作,上线后却出现CORS错误。这就是因为协议不同导致不同源,浏览器拦截了请求。
CORS机制就像国际快递的清关流程。当你从海外购买商品时:
一个真实的请求流程是这样的:
javascript复制// 前端代码
fetch('https://api.example.com/data', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
浏览器会自动添加Origin头:
code复制GET /data HTTP/1.1
Origin: https://your-site.com
Host: api.example.com
服务器需要响应这些头信息:
code复制Access-Control-Allow-Origin: https://your-site.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
非简单请求会触发预检(preflight),这就像重要访客需要提前登记。我曾在项目中使用自定义头X-Auth-Token,结果发现每次请求都会先发OPTIONS请求。
预检请求示例:
code复制OPTIONS /resource HTTP/1.1
Origin: https://your-site.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: X-Auth-Token
服务器必须明确允许这些操作:
code复制Access-Control-Allow-Origin: https://your-site.com
Access-Control-Allow-Methods: GET, POST, DELETE
Access-Control-Allow-Headers: X-Auth-Token
Access-Control-Max-Age: 86400 // 缓存1天
在Spring Boot中,我推荐使用WebMvcConfigurer统一配置:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("https://your-domain.com")
.allowedMethods("*")
.allowedHeaders("*")
.exposedHeaders("X-Custom-Header")
.allowCredentials(true)
.maxAge(3600);
}
}
注意几个易错点:
allowCredentials(true)时不能使用allowedOrigins("*")这是我最推荐的零侵入方案,配置示例:
nginx复制server {
listen 80;
server_name api.your-domain.com;
location / {
proxy_pass http://backend-service;
# 处理预检请求
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin "$http_origin";
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization, Content-Type";
add_header Access-Control-Allow-Credentials "true";
add_header Content-Length 0;
add_header Content-Type text/plain;
return 204;
}
add_header Access-Control-Allow-Origin "$http_origin";
add_header Access-Control-Allow-Credentials "true";
add_header Access-Control-Expose-Headers "X-Custom-Header";
}
}
当涉及Cookie和认证时,CORS有严格限制。我在电商项目中就遇到过这样的场景:用户登录态需要跨子域共享。
正确做法是:
withCredentialsjavascript复制fetch('https://api.example.com/auth', {
credentials: 'include'
})
code复制Access-Control-Allow-Origin: https://shop.example.com
Access-Control-Allow-Credentials: true
Set-Cookie: token=xxx; Domain=.example.com; Secure; SameSite=None
随着安全要求提高,Chrome 80+版本对Cookie策略做了重大调整:
SameSite=Lax,会阻止跨站CookieSameSite=None; Secure才能跨域传输SameSite=None这导致很多历史项目突然出现跨域问题。我的解决经验是: