作为一名长期使用若依框架进行企业级开发的老手,我处理过的跨域问题不下百例。跨域问题本质上是一个浏览器安全机制问题,而非技术缺陷。现代浏览器出于安全考虑,会强制执行同源策略(Same-Origin Policy),这是问题的根源所在。
同源策略要求三个关键要素必须完全一致:
比如:
http://a.com 与 https://a.com → 不同源(协议不同)http://a.com 与 http://b.com → 不同源(域名不同)http://a.com:80 与 http://a.com:8080 → 不同源(端口不同)在若依前后端分离项目中,最常见的跨域场景是:
http://localhost:80http://localhost:8080此时浏览器控制台会报错:
code复制Access to XMLHttpRequest at 'http://localhost:8080/api/login' from origin 'http://localhost:80' has been blocked by CORS policy
实际开发中,我曾遇到一个典型案例:某企业使用Nginx反向代理,前端部署在
https://www.example.com,后端API在https://api.example.com,虽然主域名相同,但子域名不同仍触发跨域限制。
若依采用Spring MVC的CORS过滤器实现跨域支持,这是目前最主流、最稳定的方案。其核心原理是通过响应头告知浏览器该请求是被允许的:
java复制// 典型CORS响应头
Access-Control-Allow-Origin: http://localhost:80
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET,POST,PUT,DELETE
若依的默认配置位于ruoyi-framework模块的CorsConfig.java:
java复制@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOriginPattern("*"); // 允许所有来源
config.setAllowCredentials(true); // 允许凭证
config.addAllowedMethod("*"); // 允许所有方法
config.addAllowedHeader("*"); // 允许所有头
config.setMaxAge(3600L); // 预检缓存时间
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
| 参数 | 说明 | 开发环境建议 | 生产环境建议 |
|---|---|---|---|
| allowedOriginPattern | 允许的源 | *(所有) |
指定具体域名 |
| allowCredentials | 允许凭证 | true | true(必须) |
| allowedMethods | 允许方法 | *(所有) |
按需限制 |
| maxAge | 预检缓存 | 3600秒 | 可适当延长 |
在实际项目部署时,我强烈建议进行以下安全优化:
java复制// 生产环境推荐配置
config.addAllowedOriginPattern("https://www.yourdomain.com");
config.addAllowedOriginPattern("https://admin.yourdomain.com");
config.addAllowedMethod("GET");
config.addAllowedMethod("POST");
config.addAllowedMethod("PUT");
config.addAllowedMethod("DELETE");
config.addExposedHeader("Authorization"); // 暴露自定义头
单体版直接使用默认配置即可,但需要注意:
ShiroConfig中放行OPTIONS请求:java复制filterChainDefinitionMap.put("/**", "anon,cors");
request.js中的配置:javascript复制withCredentials: true // 必须为true
微服务版需要在网关层统一配置(Gateway模块的GatewayConfig.java):
java复制@Bean
public CorsWebFilter corsWebFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOriginPattern("*");
config.setAllowCredentials(true);
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
重要提示:微服务版必须删除各业务模块中的CORS配置,否则会导致响应头重复!
若依前端(vue-element-admin)需要特别注意以下配置:
src/utils/request.js中的关键设置:javascript复制const service = axios.create({
withCredentials: true, // 必须开启
timeout: 5000
})
.env.development):properties复制VUE_APP_BASE_API = '/dev-api' # 开发环境代理前缀
vue.config.js):javascript复制devServer: {
proxy: {
'/dev-api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: {
'^/dev-api': ''
}
}
}
}
code复制The value of the 'Access-Control-Allow-Origin' header must not be the wildcard '*'
解决方案:
addAllowedOriginPattern替代addAllowedOriginOPTIONS请求返回403/401:
java复制filterChainDefinitionMap.put("/**", "anon,cors");
java复制config.addExposedHeader("Token");
config.addExposedHeader("Custom-Header");
javascript复制headers: {
'Custom-Header': 'value'
}
bash复制curl -I -X OPTIONS http://api.example.com/endpoint \
-H "Origin: http://frontend.example.com" \
-H "Access-Control-Request-Method: POST"
maxAge:java复制config.setMaxAge(86400L); // 24小时缓存
java复制// 支持正则表达式匹配
config.addAllowedOriginPattern("https://*.example.com");
java复制config.addAllowedMethod("GET");
config.addAllowedMethod("POST");
java复制// 对特定路径使用更严格的策略
CorsConfiguration strictConfig = new CorsConfiguration();
strictConfig.addAllowedOrigin("https://admin.example.com");
source.registerCorsConfiguration("/api/admin/**", strictConfig);
通过Profile实现环境差异化:
java复制@Profile("dev")
@Bean
public CorsFilter devCorsFilter() {
// 开发环境宽松配置
}
@Profile("prod")
@Bean
public CorsFilter prodCorsFilter() {
// 生产环境严格配置
}
从数据库读取允许的源:
java复制@Bean
public CorsFilter dynamicCorsFilter(OriginConfigService configService) {
CorsConfiguration config = new CorsConfiguration();
configService.getAllowedOrigins().forEach(config::addAllowedOriginPattern);
// 其他配置...
}
网关层统一控制 + 业务模块特殊配置:
java复制// 网关全局配置
config.addAllowedOriginPattern("*");
// 特定微服务额外配置
@Bean
public CorsFilter strictCorsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOriginPattern("https://special.example.com");
// 注册到特定路径
}
误区:Nginx配置替代后端CORS
误区:过度依赖通配符
*会导致安全风险误区:忽略预检请求缓存
微服务版特殊注意
浏览器开发者工具检查:
code复制Access-Control-Allow-Origin: [预期值]
Access-Control-Allow-Credentials: true
全链路测试:
java复制@Test
public void testCorsHeaders() {
mockMvc.perform(options("/api/test")
.header("Origin", "http://allowed.com")
.header("Access-Control-Request-Method", "POST"))
.andExpect(header().exists("Access-Control-Allow-Origin"));
}
JSONP(已淘汰):
代理方案:
CORS标准方案:
更精细的权限控制:
安全增强:
协议层优化:
在实际项目开发中,我建议团队建立跨域配置规范,将最佳实践纳入项目脚手架。对于大型分布式系统,可以考虑开发统一的CORS管理组件,通过配置中心动态调整策略。