Java跨域处理完整案例
什么是跨域问题
当浏览器发起AJAX请求时,如果请求的URL与当前页面的协议、域名、端口任一不同,就产生了跨域,浏览器出于安全考虑,默认会阻止跨域请求。

跨域示例:
- 页面地址:
http://localhost:8080 - 请求地址:
http://localhost:9090/api/data
解决方案对比
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| CORS过滤器 | Spring Boot/Spring MVC | 简单、无需改动业务代码 | 需要服务端配合 |
| @CrossOrigin注解 | 单个Controller/方法 | 精确控制 | 需要逐类/方法配置 |
| 代理转发 | 前后端分离开发 | 无需服务端配置 | 仅限开发环境 |
| JSONP | 老项目 | 兼容性好 | 只支持GET请求 |
完整案例实现
1 CORS全局配置(推荐)
实现WebMvcConfigurer接口
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 匹配所有路径
.allowedOriginPatterns("*") // 允许所有来源
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.exposedHeaders("Authorization", "Content-Type")
.allowCredentials(true) // 允许携带Cookie
.maxAge(3600); // 预检请求缓存时间(秒)
}
}
使用CorsFilter(更底层)
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// 允许的域名,可以使用通配符*
config.setAllowedOriginPatterns(Arrays.asList(
"http://localhost:8080",
"http://localhost:3000",
"https://*.example.com"
));
// 允许的请求头
config.setAllowedHeaders(Arrays.asList(
"Authorization",
"Content-Type",
"X-Requested-With",
"Accept",
"Origin",
"Access-Control-Request-Method",
"Access-Control-Request-Headers"
));
// 暴露的响应头
config.setExposedHeaders(Arrays.asList(
"Authorization",
"Content-Type",
"X-Session-Id"
));
// 允许的请求方法
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"));
// 是否允许携带凭证(Cookie等)
config.setAllowCredentials(true);
// 预检请求的缓存时间
config.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
2 自定义跨域拦截器
@Component
public class CorsInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 设置响应头,允许跨域访问
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Max-Age", "3600");
// 处理预检请求
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpStatus.OK.value());
return false; // 不继续处理
}
return true;
}
}
3 注解方式(精确控制)
@RestController
@RequestMapping("/api")
// 类级别的跨域配置
@CrossOrigin(origins = "http://localhost:8080", maxAge = 3600)
public class UserController {
// 方法级别的跨域配置(优先级高于类级别)
@GetMapping("/users")
@CrossOrigin(
origins = {"http://localhost:8080", "http://localhost:3000"},
allowedHeaders = {"*"},
methods = {RequestMethod.GET, RequestMethod.POST}
)
public List<User> getUsers() {
return userService.findAll();
}
@PostMapping("/login")
public Result login(@RequestBody LoginRequest request) {
// 登录逻辑
return Result.success();
}
}
4 Spring Security集成方案
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and() // 启用CORS支持
.csrf().disable() // 生产环境需谨慎
.authorizeRequests()
.antMatchers("/api/**").permitAll()
.anyRequest().authenticated();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:8080"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
前端跨域处理
1 Axios配置
// axios.js
import axios from 'axios'
const service = axios.create({
baseURL: '/api',
timeout: 15000,
withCredentials: true // 携带Cookie
})
// 请求拦截器
service.interceptors.request.use(
config => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
return response.data
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
// 处理未授权
router.push('/login')
break
case 403:
// 处理禁止访问
break
default:
break
}
}
return Promise.reject(error)
}
)
export default service
2 Vue/React配置代理
Vue.config.js:
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:9090', // 后端地址
changeOrigin: true, // 改变请求源
pathRewrite: {
'^/api': '' // 重写路径
}
}
}
}
}
常见问题处理
1 携带Cookie的跨域请求
// 后端配置
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// 注意:使用allowCredentials时,不能使用 "*" 作为origin
config.setAllowedOriginPatterns(Arrays.asList("http://localhost:8080"));
config.setAllowCredentials(true);
config.addAllowedHeader("*");
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
2 预检请求(OPTIONS)
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public Result handleException(Exception e) {
// 处理预检请求
if (e instanceof HttpRequestMethodNotSupportedException) {
return Result.error(405, "请求方式不支持");
}
return Result.error(500, e.getMessage());
}
}
最佳实践建议
- 生产环境:采用CORS过滤器方案,配合Spring Security
- 开发环境:使用代理转发,避免跨域问题
- 安全考虑:
- 不要使用通配符,指定具体域名
- 谨慎使用
allowCredentials(true) - 设置合理的预检请求缓存时间
- 性能优化:
- 减少预检请求次数
- 合理设置
maxAge - 使用HTTP缓存
测试跨域配置
# 使用curl测试跨域请求 curl -X OPTIONS http://localhost:8080/api/users \ -H "Origin: http://localhost:3000" \ -H "Access-Control-Request-Method: GET" \ -v # 检查响应头 # Access-Control-Allow-Origin: http://localhost:3000 # Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS # Access-Control-Allow-Headers: Authorization, Content-Type
通过以上方案,可以完整解决Java项目中的跨域问题,让前后端分离开发更加顺畅。