SpringBoot实战:从同源策略到CORS,一站式解决前端跨域请求难题

张开发
2026/4/16 10:21:03 15 分钟阅读

分享文章

SpringBoot实战:从同源策略到CORS,一站式解决前端跨域请求难题
1. 浏览器同源策略为什么你的前端请求被拦截了第一次遇到跨域问题时我盯着浏览器控制台那个鲜红的CORS错误提示整整发呆了十分钟。明明后端接口已经返回了200状态码前端却死活拿不到数据。后来才发现这都是浏览器同源策略在作怪。同源策略就像小区门禁系统要求访客必须满足三个条件才能放行相同的协议如http/https、相同的域名、相同的端口号。比如http://a.com和http://a.com/api同源协议域名端口相同http://a.com和https://a.com不同源协议不同http://a.com:8080和http://a.com:3000不同源端口不同这个策略本质上是为了保护用户数据安全。想象这样一个场景你在A网站登录了银行账号然后不小心点开了恶意网站B。如果没有同源策略B网站就能随意调用银行API获取你的账户信息。我在实际项目中就遇到过因为错误配置CORS导致用户敏感信息泄露的安全事件。有趣的是同源策略只存在于浏览器环境。如果你用Postman或者curl测试接口根本不会遇到跨域问题。这也是很多新手容易困惑的地方——明明Postman能调通的接口为什么前端就是拿不到数据2. CORS机制浏览器与服务器的安全握手当浏览器发现当前页面与请求目标不同源时会触发CORS跨域资源共享机制。这个过程就像一场精心设计的握手协议简单请求直接发送实际请求如GET/HEAD/POST且Content-Type为text/plain预检请求非简单请求前会先发OPTIONS请求探路我曾用Chrome开发者工具抓包完整观察过这个流程。一个带自定义Header的POST请求会先发出OPTIONS请求OPTIONS /api/user HTTP/1.1 Origin: http://localhost:3000 Access-Control-Request-Method: POST Access-Control-Request-Headers: X-Custom-Header服务器需要响应这些关键HeaderHTTP/1.1 200 OK Access-Control-Allow-Origin: http://localhost:3000 Access-Control-Allow-Methods: POST, GET, OPTIONS Access-Control-Allow-Headers: X-Custom-Header Access-Control-Max-Age: 86400常见的坑点包括忘记处理OPTIONS请求导致预检失败Access-Control-Allow-Origin设置为*但同时又要求携带cookie没有正确配置Vary: Origin头部导致缓存污染3. SpringBoot中的CORS配置实战在SpringBoot项目中我通常推荐三种配置方式各有适用场景3.1 全局配置类生产环境推荐Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOrigins(https://yourdomain.com) .allowedMethods(GET, POST) .allowCredentials(true) .maxAge(3600); } }这种方式的优势是可以集中管理规则我在电商项目中就用它来区分开放API允许任意源访问管理后台API只允许内网访问支付接口严格限定合作方域名3.2 注解方式开发环境快捷方案RestController RequestMapping(/api) CrossOrigin(origins http://localhost:3000) public class UserController { // 控制器方法 }虽然方便但要注意别在生产环境滥用。有次线上故障就是因为开发同学在Controller上大量使用CrossOrigin导致安全团队扫描出漏洞。3.3 过滤器方案特殊需求Component public class CorsFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) { HttpServletResponse response (HttpServletResponse) res; response.setHeader(Access-Control-Allow-Origin, https://trusted.com); // 其他Header配置... chain.doFilter(req, res); } }适合需要动态判断源的情况比如根据请求参数动态设置允许的Origin。我在多租户SaaS项目中就用这种方案实现租户间的资源隔离。4. 前端联调中的常见问题排查即使后端配置正确前端也可能遇到各种诡异问题。这里分享几个实战案例案例1Chrome正常但Safari报错问题出在Safari对credentials的处理更严格。解决方案是确保后端设置allowCredentials(true)前端fetch请求设置credentials: includeAccess-Control-Allow-Origin不能为*案例2POST请求变成OPTIONS这是典型的预检请求未通过。检查是否配置了OPTIONS方法自定义Header是否在allowedHeaders中列出响应头是否包含所有请求头案例3带Cookie的请求失败需要满足三个条件后端allowCredentials(true)前端withCredentialstrueAccess-Control-Allow-Origin必须是具体域名而非*我曾用下面这个测试脚本快速验证配置fetch(http://api.example.com/data, { method: POST, credentials: include, headers: { Content-Type: application/json, X-Requested-With: XMLHttpRequest }, body: JSON.stringify({test: 123}) }) .then(response response.json()) .then(data console.log(data));5. 安全加固别让CORS成为你的阿喀琉斯之踵CORS配置不当可能导致严重安全漏洞分享几个防护经验白名单优于通配符永远不要轻易使用allowedOrigins(*)我见过太多项目因为这个设置导致CSRF攻击。推荐做法.allowedOriginPatterns( https://*.yourdomain.com, https://partner.com )严格限制HTTP方法不需要PUT/DELETE的接口就不要开放减少攻击面。敏感接口禁用CORS像/admin/**这类接口应该完全禁用跨域访问。结合其他安全措施重要操作要求CSRF Token敏感数据接口添加二次认证监控异常的Origin请求有次安全审计中我们发现攻击者利用宽松的CORS配置配合XSS漏洞窃取用户数据。后来我们引入了动态Origin验证机制Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOriginPatterns(loadAllowedOrigins()) // 其他配置... } }; } private String[] loadAllowedOrigins() { // 从数据库或配置中心动态加载 }6. 高级场景微服务架构下的CORS方案在微服务环境中CORS配置会更复杂。我们曾采用过三种方案方案1API网关统一处理在Gateway层配置全局CORS规则优点是各服务无需关心跨域问题。Nginx配置示例location /api { if ($http_origin ~* (https://.*\.yourdomain.com)) { add_header Access-Control-Allow-Origin $http_origin; add_header Access-Control-Allow-Methods GET,POST; } # 其他配置... }方案2Spring Cloud Gateway过滤器适合Spring技术栈Bean public CorsWebFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.applyPermitDefaultValues(); config.setAllowCredentials(true); config.addAllowedOrigin(https://portal.yourdomain.com); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsWebFilter(source); }方案3服务网格Sidecar代理在Istio中可以通过VirtualService配置apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: product-service spec: hosts: - product-service http: - corsPolicy: allowOrigins: - exact: https://web.yourdomain.com allowMethods: - GET - POST route: - destination: host: product-service每种方案都有适用场景我们最终选择了网关统一处理关键服务独立配置的混合模式。在Kubernetes环境中还需要注意Ingress Controller的CORS配置可能会覆盖应用层设置。

更多文章