1. 为什么需要处理跨域问题跨域问题源于浏览器的同源策略Same-Origin Policy这是现代浏览器最基本的安全机制之一。简单来说当你的前端页面比如运行在http://localhost:8080尝试通过AJAX请求后端API比如http://api.example.com时浏览器会阻止这种请求除非后端明确允许。在实际开发中前后端分离架构已经成为主流前端可能部署在CDN或独立域名下后端API则有专门的域名。这种情况下跨域问题几乎不可避免。我曾接手过一个电商项目前端使用Vue.js部署在static.shop.com而后端API在api.shop.com就因为没处理好CORS导致支付功能完全无法使用。提示即使前端和后端在同一台服务器如果端口不同如前端3000端口后端8080端口也会触发跨域限制。2. Sa-Token全局过滤器基础配置2.1 添加Sa-Token依赖首先需要在项目中引入Sa-Token核心库。以Maven项目为例dependency groupIdcn.dev33/groupId artifactIdsa-token-spring-boot-starter/artifactId version1.45.0/version /dependency我建议始终使用最新稳定版因为Sa-Token团队会持续修复CORS相关的边界问题。曾经有个项目使用了较旧的1.38.0版本在处理OPTIONS预检请求时会出现奇怪的拦截行为。2.2 创建基础过滤器类创建一个继承自SaServletFilter的全局过滤器Configuration public class SaTokenFilter implements SaServletFilter { Override public void addExcludes(String... paths) { // 不需要过滤的路径 } Override public void setAuth(Object handler) { // 认证逻辑 } Override public void setBeforeAuth(Object handler) { // 前置处理 } }这个基础结构是三种解决方案的共同起点。注意setBeforeAuth方法这是我们后续处理CORS的关键切入点。3. 方案一纯Sa-Token过滤器实现3.1 核心代码实现在setBeforeAuth方法中添加CORS处理逻辑Override public void setBeforeAuth(Object handler) { HttpServletRequest request (HttpServletRequest) handler; HttpServletResponse response (HttpServletResponse) handler; // 设置允许的源 response.setHeader(Access-Control-Allow-Origin, *); // 允许的请求方法 response.setHeader(Access-Control-Allow-Methods, POST, GET, OPTIONS, DELETE); // 预检请求缓存时间 response.setHeader(Access-Control-Max-Age, 3600); // 允许的请求头 response.setHeader(Access-Control-Allow-Headers, x-requested-with, sa-token, Content-Type); }3.2 实际项目中的坑点星号(*)限制虽然设置为*很方便但在需要传递cookie时比如单点登录场景必须指定具体域名且要设置Access-Control-Allow-Credentials: true。我曾在SSO集成时花了半天排查为什么cookie不生效。OPTIONS请求处理浏览器会先发送OPTIONS预检请求。很多开发者只处理了GET/POST却漏了OPTIONS导致PUT/DELETE等方法失败。务必确保所有方法都被列出。Header大小写敏感有些前端框架会自动转换header名称大小写而后端可能区分大小写。建议统一使用小写比如sa-token而非Sa-Token。4. 方案二结合Spring的CorsRegistry4.1 混合配置方式如果你同时使用Spring MVC可以结合两种方案Configuration public class WebMvcConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOrigins(*) .allowedMethods(*) .allowedHeaders(*) .exposedHeaders(sa-token); } } // 同时在SaTokenFilter中保留基础CORS设置这种组合方式特别适合大型项目可以利用Spring更细粒度的路径控制同时保持Sa-Token的认证逻辑。4.2 优先级问题Spring的CORS配置和Sa-Token过滤器的执行顺序很重要。实测发现简单请求GET/POST先经过Spring CORS再到Sa-Token复杂请求PUT/DELETE等OPTIONS请求会被Spring优先处理这意味着如果你的Sa-Token过滤器对OPTIONS请求有特殊逻辑可能需要调整。一个保险的做法是在SaTokenFilter中增加判断if (OPTIONS.equals(request.getMethod())) { return; }5. 方案三基于注解的精细控制5.1 SaCheckCors注解实现对于需要特殊CORS规则的接口可以创建自定义注解Retention(RetentionPolicy.RUNTIME) Target(ElementType.METHOD) public interface SaCheckCors { String[] origins() default *; String[] methods() default {GET, POST}; String[] headers() default {}; }然后在过滤器中解析注解HandlerMethod handlerMethod (HandlerMethod)handler; SaCheckCors corsConfig handlerMethod.getMethodAnnotation(SaCheckCors.class); if (corsConfig ! null) { response.setHeader(Access-Control-Allow-Origin, String.join(,, corsConfig.origins())); // 其他header设置... }5.2 实际应用场景这种方案特别适合某些接口需要开放给特定合作伙伴域名不同接口支持不同的HTTP方法需要动态调整CORS策略的灰度发布场景比如支付回调接口可能只允许来自支付平台的POST请求PostMapping(/pay/callback) SaCheckCors(origins https://payment-platform.com, methods POST) public Result payCallback() { // ... }6. 生产环境最佳实践6.1 安全加固建议不要无脑使用*根据项目实际需要设置允许的源可以配置在application.yml中sa-token: cors: allowed-origins: - https://www.yourdomain.com - https://admin.yourdomain.com限制暴露的Header只暴露必要的Header避免泄露敏感信息response.setHeader(Access-Control-Expose-Headers, sa-token, content-length);结合HTTPSCORSHTTP是不安全的组合生产环境必须启用HTTPS。6.2 性能优化合理设置Max-Age对于稳定的API可以设置较长的缓存时间减少OPTIONS请求response.setHeader(Access-Control-Max-Age, 86400); // 24小时避免重复处理在过滤器中添加判断已经处理过的请求不再重复设置Headerif (response.getHeader(Access-Control-Allow-Origin) ! null) { return; }网关层统一处理如果项目使用微服务架构可以考虑在API网关统一处理CORS避免每个服务重复配置。7. 疑难问题排查指南7.1 常见错误现象控制台报错但服务端日志无记录通常是浏览器拦截了请求根本未到达服务端。检查网络面板确认请求是否真的发出。POST变成OPTIONS这是正常预检机制检查你的服务是否正确响应了OPTIONS方法。明明配置了CORS还是报错检查是否有多个过滤器/拦截器覆盖了CORS头使用curl测试确认响应头是否正确curl -I -X OPTIONS http://your-api.com/endpoint7.2 调试技巧浏览器无痕模式测试避免缓存干扰Chrome的隐身窗口是最干净的测试环境。简化复现创建一个最小化的测试接口排除业务逻辑干扰GetMapping(/test-cors) public String testCors() { return CORS test; }查看完整请求链路使用Charles或Fiddler抓包对比请求和响应的完整Header信息。8. 三种方案对比与选型建议8.1 方案对比表特性纯Sa-Token方案Spring混合方案注解方案实现复杂度低中高灵活性一般较好最好性能影响小较小中等适合场景简单项目Spring项目需要精细控制维护成本低中较高8.2 个人经验建议根据我参与过的十几个项目经验初创项目/快速原型选择方案一简单直接10分钟搞定。标准Spring Boot应用方案二最平衡既利用Spring生态又保持Sa-Token特性。企业级复杂系统特别是需要对接多第三方系统时方案三的精细控制必不可少。一个实际案例我们有个金融项目需要对接5个外部支付渠道每个渠道的CORS要求都不同最终采用方案三为每个渠道接口单独配置大大降低了维护难度。