Java案例实战解决CORS问题的完整指南
目录导读
- 跨域报错是什么?为什么需要解决?
- Java后端跨域问题的常见场景与根源
- 基于Spring Boot的跨域配置方案(含代码案例)
- 自定义过滤器解决跨域报错(XML & 注解两种方式)
- 前端配合:如何避免同源策略导致的请求失败?
- 常见问答:跨域配置后依然报错怎么办?
- 从原理到实战的跨域解决全景图
跨域报错是什么?为什么需要解决?
在Web开发中,浏览器出于安全考虑,实施了同源策略,所谓“同源”,指协议、域名、端口号三者完全一致,当页面发起的请求目标与页面本身来源不一致时,浏览器会阻止跨域HTTP请求的响应数据被JavaScript读取,并在控制台显示典型错误:

Access to XMLHttpRequest at 'http://api.example.com/data' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
这个错误在前后端分离架构中尤其常见,前端跑在localhost:3000,后端Java服务跑在localhost:8080,两者端口不同,即构成跨域。不解决跨域,前端就无法正常调用后端API获取数据,项目无法运行。
Java后端跨域问题的常见场景与根源
场景1:开发环境前后端分离
- 前端:Vue/React项目,运行在
http://localhost:5173 - 后端:Spring Boot 项目,运行在
http://localhost:8080 - 根源:端口不同,浏览器拒绝读取响应
场景2:不同域名下的微服务调用
- 前端页面来自
https://app.yourdomain.com - 后端API托管在
https://api.anotherdomain.com - 根源:域名不同
场景3:前端通过CDN访问后端
- 前端资源在
https://cdn.yourdomain.com - 后端API在
https://api.yourdomain.com - 根源:子域名不同,同样属于“跨域”
根本原因:浏览器自动发出OPTIONS预检请求(针对非简单请求),检查服务器是否允许跨域,如果服务器响应中缺少Access-Control-Allow-Origin等头部,浏览器就拒绝真正的请求。
基于Spring Boot的跨域配置方案(含代码案例)
这是目前Java项目中最主流的方法,Spring Boot提供了多种优雅的CORS配置方式。
方法1:全局CORS配置(推荐)
使用WebMvcConfigurer接口:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class GlobalCorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**") // 允许跨域的路径
.allowedOrigins("http://localhost:5173") // 允许的源
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的方法
.allowedHeaders("*") // 允许的请求头
.allowCredentials(true) // 允许携带Cookie
.maxAge(3600); // 预检请求缓存时间
}
};
}
}
关键点:
allowedOrigins可配置多个源,生产环境建议写具体域名,不要用(尤其当allowCredentials(true)时,不合法)maxAge可减少OPTIONS预检请求次数,提升性能
方法2:注解方式(适合单个Controller或方法)
使用@CrossOrigin注解,直接加在Controller类或方法上:
@RestController
@CrossOrigin(origins = "http://localhost:5173")
public class UserController {
@GetMapping("/api/user/info")
public UserInfo getUserInfo() {
// 业务逻辑
}
}
缺点:每个需要跨域的类都要注解,维护成本高,不适合大型项目。
自定义过滤器解决跨域报错(XML & 注解两种方式)
如果项目不是Spring Boot(例如传统Spring MVC或需精细控制请求/响应头),可以手动写一个过滤器。
Java注解方式(过滤器)
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "http://localhost:5173");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Max-Age", "3600");
// 特别注意:对于OPTIONS预检请求,直接返回200
if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(req, res);
}
}
XML方式(传统Spring项目)
在web.xml中配置过滤器:
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>com.example.CorsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/api/*</url-pattern>
</filter-mapping>
过滤器方式的优势:对请求和响应头有完全控制权,适合需要动态设置allowedOrigins(如从数据库读取白名单)的场景。
前端配合:如何避免同源策略导致的请求失败?
后端配置只是让服务器“允许”跨域,前端也需要做正确动作,否则依然可能报错。
使用Fetch API时不要忽略credentials
fetch('http://localhost:8080/api/user/info', {
method: 'GET',
credentials: 'include' // 如果后端允许携带Cookie,前端必须显式设置
})
.then(response => response.json())
.then(data => console.log(data));
Axios中设置withCredentials
axios.get('http://localhost:8080/api/user/info', {
withCredentials: true
}).then(response => {
console.log(response.data);
});
生产环境使用反向代理(推荐)
在生产部署时,不要直接把前端代码放在CDN裸连后端,而是通过Nginx配置反向代理,让前端访问的是同源路径:
server {
listen 80;
server_name www.yourdomain.com;
location /api/ {
proxy_pass http://backend-server:8080/api/;
}
location / {
root /static/frontend;
}
}
这样前端所有请求都指向www.yourdomain.com/api/...,浏览器不再跨域,彻底根除问题。
常见问答
Q1:配置了CORS,为什么还是报错? 可能原因:
- 后端配置的
allowedOrigins与前端实际源不一致(例如端口不同,或http与https混用) - 使用了
allowCredentials(true)但allowedOrigins写了,这会报错(浏览器限制) - 请求是非简单请求但后端未正确处理
OPTIONS预检请求(过滤器或Spring配置遗漏) - 前端请求头中带了自定义Header,但后端未在
allowedHeaders中允许
Q2:如何同时允许多个域名跨域? 在Spring Boot中,可以传入数组或列表:
.allowedOrigins("http://localhost:5173", "https://app.yourdomain.com")
或使用allowedOriginPatterns("*")(Spring 2.4+支持通配符),但不推荐生产环境使用。
Q3:CORS安全吗? CORS本质是浏览器端的策略,它防不住非浏览器的HTTP客户端(如Postman、curl),后端依然要做好鉴权(JWT、Session等),CORS只是让浏览器“允许”JavaScript读取跨域响应,而非授权访问。
*Q4:为什么开发时用`可以,正式发布不行?** 因为使用allowCredentials(true)时,""被浏览器禁止,且生产环境暴露`意味着任何来源都可以跨域访问,有安全风险,建议维护一个白名单列表。
Q5:如果是Spring Cloud Gateway等网关,如何配置? 网关同样需要配置CORS,例如在Gateway的路由配置中加上:
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "http://localhost:5173"
allowedMethods: "*"
从原理到实战的跨域解决全景图
跨域报错看似复杂,但核心只需三步:
- 后端允许:通过Spring Boot全局配置、过滤器或网关,返回正确的CORS响应头
- 前端配合:正确设置
credentials,生产环境使用反向代理 - 错误排查:检查
allowedOrigins、OPTIONS处理、Header白名单是否对齐
最佳实践建议:
- 开发环境使用Spring Boot的
@CrossOrigin或全局配置,简单快捷 - 生产环境推荐Nginx反向代理,从根源上消除跨域,同时提升性能
- 如果必须使用CORS跨域,请严格限定
allowedOrigins白名单,并启用maxAge缓存预检响应
通过以上Java案例和完整配置,你应该能轻松解决任何Java Web项目中的跨域报错问题。跨域不是bug,是浏览器的安全机制——理解原理后,配置只是几分钟的事。