Spring Boot多租户认证问题:TenantFilter需在JWT过滤器前执行
解决Spring Boot多租户过滤器在JWT过滤器前执行的问题
核心问题分析
你的TenantFilter当前使用@Order(Ordered.LOWEST_PRECEDENCE - 100),这个优先级数值非常高(Ordered.LOWEST_PRECEDENCE为Integer.MAX_VALUE),意味着它会在绝大多数过滤器之后执行。而Spring Security的核心过滤器FilterChainProxy默认优先级为-100,会优先于你的租户过滤器处理请求,导致JWT认证/授权时租户数据库连接还未初始化。
下面提供两种可靠的解决方案:
方案一:调整租户过滤器的全局优先级
直接修改TenantFilter的@Order值,让它的优先级高于Spring Security的过滤器链:
@Order(-101) // 数值小于-100,确保在Spring Security过滤器前执行 public class TenantFilter extends OncePerRequestFilter { private static final Logger logger = LoggerFactory.getLogger(TenantFilter.class); private static final String TENANT_HEADER = "X-Tenant"; private static final String CONNECTION_STRING = "ObfuscatedConnectionString"; private static final String TENANT_REPLACEMENT = "TENANT"; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 补充:从请求头获取租户ID(原代码此处存在逻辑缺失) String tenantId = request.getHeader(TENANT_HEADER); if (tenantId == null || tenantId.isBlank()) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "X-Tenant请求头不能为空"); return; } String dbConnectionString = CONNECTION_STRING.replace(TENANT_REPLACEMENT, tenantId); ConnectionStorage.setConnection(dbConnectionString); try { filterChain.doFilter(request, response); } finally { // 确保无论请求是否异常,连接都会被清理 ConnectionStorage.clear(); } } }
同时确保过滤器被Spring容器管理,可通过@Component注解或配置类注册:
@Configuration public class FilterConfig { @Bean public TenantFilter tenantFilter() { return new TenantFilter(); } }
方案二:将租户过滤器嵌入Spring Security过滤器链
如果不想依赖全局优先级,可在Security配置中显式指定TenantFilter在JWT过滤器之前执行:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() // 先执行租户过滤器,再处理JWT认证/授权 .addFilterBefore(tenantFilter(), JwtAuthenticationFilter.class) .addFilterBefore(tenantFilter(), JwtAuthorizationFilter.class) .addFilter(new JwtAuthenticationFilter(authenticationManagerBean())) .addFilter(new JwtAuthorizationFilter(authenticationManagerBean())) .authorizeRequests() .antMatchers("/auth/login").permitAll() // 放行登录接口 .anyRequest().authenticated(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public TenantFilter tenantFilter() { return new TenantFilter(); } }
这种方式更精准,直接在Spring Security的过滤器链中定义顺序,避免全局优先级冲突。
内容的提问来源于stack exchange,提问作者ShadowFlame
相关产品推荐
相关产品推荐

