You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 15:55:19