Sa-Token全局过滤器解决跨域问题的三种实践方案

发布时间:2026/7/18 7:41:35
Sa-Token全局过滤器解决跨域问题的三种实践方案 1. 项目概述作为一名经历过多次跨域问题折磨的后端开发者我深知这个看似简单的问题在实际项目中能带来多大的困扰。最近在重构一个前后端分离项目时我尝试了Sa-Token框架提供的全局过滤器解决方案意外发现它竟然能如此优雅地处理跨域问题。今天就来分享三种基于Sa-Token全局过滤器的跨域解决方案这些方案都是我在实际项目中验证过的可以直接拿来使用。跨域问题本质上是浏览器出于安全考虑实施的同源策略限制。当你的前端应用运行在http://localhost:8080而后端API部署在http://api.example.com时浏览器就会拦截这些跨域请求。传统的解决方案往往需要在每个Controller上添加CrossOrigin注解或者配置繁琐的WebMvcConfigurer而Sa-Token的全局过滤器提供了一种更统一、更灵活的处理方式。2. 核心原理与技术选型2.1 为什么选择全局过滤器全局过滤器的优势在于它能拦截所有进入应用的请求在请求到达Controller之前就完成跨域处理。相比传统的CrossOrigin注解方式它有三大优势统一管理不需要在每个Controller上重复添加注解灵活配置可以动态调整跨域策略前置处理能正确处理OPTIONS预检请求Sa-Token的全局过滤器本质上是一个Servlet Filter它通过实现StpFilterInterface接口可以无缝集成到Sa-Token的权限认证体系中。2.2 Sa-Token过滤器的工作原理Sa-Token的过滤器链执行顺序如下客户端请求 - Sa-Token全局过滤器 - 业务逻辑处理 - 响应返回在过滤器层面处理跨域意味着我们可以在请求到达业务代码前就设置好CORS相关的响应头这对于OPTIONS预检请求的处理尤为重要。当浏览器发送复杂跨域请求时会先发送一个OPTIONS请求探路只有收到正确的CORS响应头后才会发送真正的请求。3. 三种实现方案详解3.1 方案一基础CORS配置这是最简单的实现方式适合大多数基础场景。创建一个配置类实现StpFilterInterfaceComponent public class CorsSaTokenFilter implements StpFilterInterface { Override public void doFilter(SaRequest request, SaResponse response, SaFilterChain chain) throws Exception { // 设置CORS头 response.setHeader(Access-Control-Allow-Origin, *); response.setHeader(Access-Control-Allow-Methods, GET, POST, PUT, DELETE, OPTIONS); response.setHeader(Access-Control-Max-Age, 3600); response.setHeader(Access-Control-Allow-Headers, Content-Type, Authorization, X-Requested-With); // 如果是OPTIONS请求直接返回 if (OPTIONS.equalsIgnoreCase(request.getMethod())) { response.setStatus(200); return; } // 继续执行过滤器链 chain.doFilter(request, response); } }关键点说明Access-Control-Allow-Origin设置为*表示允许所有域名访问生产环境建议替换为具体的域名列表以提高安全性。3.2 方案二动态域名白名单对于需要限制访问来源的生产环境我们可以实现动态域名白名单Component public class DynamicCorsFilter implements StpFilterInterface { // 允许的域名白名单 private static final ListString ALLOWED_ORIGINS Arrays.asList( https://example.com, https://admin.example.com, http://localhost:8080 ); Override public void doFilter(SaRequest request, SaResponse response, SaFilterChain chain) throws Exception { String origin request.getHeader(Origin); if (ALLOWED_ORIGINS.contains(origin)) { response.setHeader(Access-Control-Allow-Origin, origin); response.setHeader(Access-Control-Allow-Credentials, true); } // 其余配置与方案一相同 response.setHeader(Access-Control-Allow-Methods, GET, POST, PUT, DELETE, OPTIONS); // ...其他头设置 if (OPTIONS.equalsIgnoreCase(request.getMethod())) { response.setStatus(200); return; } chain.doFilter(request, response); } }注意事项当设置了Access-Control-Allow-Credentials为true时Access-Control-Allow-Origin不能为*必须指定具体域名。3.3 方案三结合Sa-Token认证的增强方案对于需要身份认证的接口我们可以将跨域处理与Sa-Token的认证逻辑结合Component public class AuthCorsFilter implements StpFilterInterface { Override public void doFilter(SaRequest request, SaResponse response, SaFilterChain chain) throws Exception { // 设置CORS头 response.setHeader(Access-Control-Allow-Origin, getAllowedOrigin(request)); response.setHeader(Access-Control-Allow-Methods, GET, POST, PUT, DELETE, OPTIONS); response.setHeader(Access-Control-Allow-Headers, Content-Type, StpUtil.getTokenName(), X-Requested-With); response.setHeader(Access-Control-Expose-Headers, StpUtil.getTokenName()); // 处理预检请求 if (OPTIONS.equalsIgnoreCase(request.getMethod())) { response.setStatus(200); return; } // 继续执行过滤器链包含Sa-Token的认证逻辑 chain.doFilter(request, response); } private String getAllowedOrigin(SaRequest request) { // 实现动态域名检查逻辑 // 可以从数据库或配置中心读取白名单 return *; // 示例简化处理 } }这个方案的关键改进是在允许的请求头中加入了Sa-Token的token名称在暴露的响应头中也加入了token信息实现了动态的域名检查逻辑4. 配置与集成指南4.1 注册过滤器在Spring Boot应用中我们需要确保Sa-Token的过滤器被正确加载。通常Sa-Token会自动配置但我们可以通过以下方式调整过滤器顺序Configuration public class SaTokenConfig { Autowired private CorsSaTokenFilter corsFilter; Bean public SaTokenFilterRegistration registerSaTokenFilter() { return new SaTokenFilterRegistration() .addFilter(corsFilter) .setOrder(Ordered.HIGHEST_PRECEDENCE); // 设置最高优先级 } }4.2 与Spring Security的兼容性如果你的项目同时使用了Spring Security需要注意过滤器执行顺序的问题。Spring Security有自己的CORS配置可能会与Sa-Token的过滤器冲突。解决方案是禁用Spring Security的CORS配置EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http.cors().disable(); // 其他安全配置... } }确保Sa-Token的过滤器在Spring Security过滤器之前执行4.3 配置文件示例在application.yml中添加Sa-Token的基础配置sa-token: # token名称 (同时也是cookie名称) token-name: satoken # token有效期单位秒 默认30天 timeout: 2592000 # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒 activity-timeout: -1 # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) is-concurrent: true # 在多人登录同一账号时是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) is-share: true5. 常见问题与解决方案5.1 预检请求(OPTIONS)被拦截现象前端发送的OPTIONS请求返回403状态码原因安全框架(如Spring Security)拦截了OPTIONS请求解决方案确保Sa-Token过滤器优先级最高在安全配置中显式放行OPTIONS请求http.authorizeRequests() .antMatchers(HttpMethod.OPTIONS).permitAll();5.2 跨域请求携带Cookie失败现象设置了withCredentialstrue但Cookie未发送解决方案服务端设置response.setHeader(Access-Control-Allow-Credentials, true);确保Access-Control-Allow-Origin不是*而是具体域名前端axios配置axios.defaults.withCredentials true;5.3 自定义请求头被拦截现象前端添加的自定义头(如X-Custom-Header)被浏览器拦截解决方案在服务端明确声明允许的请求头response.setHeader(Access-Control-Allow-Headers, Content-Type, Authorization, X-Requested-With, X-Custom-Header);5.4 性能优化建议对于高并发场景可以考虑以下优化缓存CORS响应设置适当的Access-Control-Max-Age值(如3600秒)使用CDN缓存OPTIONS请求响应将白名单配置放在缓存中避免每次请求都查询数据库6. 进阶应用场景6.1 多环境差异化配置在实际项目中我们通常需要为不同环境设置不同的CORS策略Profile(dev) Component public class DevCorsFilter implements StpFilterInterface { // 开发环境允许所有来源 } Profile(prod) Component public class ProdCorsFilter implements StpFilterInterface { // 生产环境严格限制来源 }6.2 结合Nginx的混合方案对于特别复杂的跨域场景可以结合Nginx和Sa-Token共同处理Nginx配置基础CORS头add_header Access-Control-Allow-Origin $http_origin; add_header Access-Control-Allow-Credentials true;Sa-Token过滤器处理细粒度的权限控制和特殊头设置这种方案既能减轻应用服务器负担又能保持灵活的权限控制。6.3 跨域的安全增强除了基础的CORS设置还应该考虑以下安全措施验证Origin头的格式防止HTTP头注入攻击对于敏感操作限制允许的HTTP方法记录异常的跨域请求用于安全审计// 示例安全的Origin验证 private boolean isValidOrigin(String origin) { if (origin null) return false; try { URI uri new URI(origin); return uri.getHost() ! null (uri.getScheme().equals(http) || uri.getScheme().equals(https)); } catch (URISyntaxException e) { return false; } }在实际项目中使用Sa-Token处理跨域问题一年多来我发现这种方案比传统方式更加灵活和统一。特别是在微服务架构中只需要在网关层配置一次所有下游服务就能自动获得跨域支持大大减少了重复配置的工作量。