多SecurityFilterChain循环创建:多租户配置实现咨询
多租户独立SecurityFilterChain实现方案
核心实现步骤
1. 定义租户配置类
通过配置绑定读取租户的OAuth2 issuer信息:
@ConfigurationProperties(prefix = "tenants") public class TenantSecurityProperties { // key: 租户名称,value: 对应OAuth2 issuer地址 private Map<String, String> oauth2; // Getter & Setter public Map<String, String> getOauth2() { return oauth2; } public void setOauth2(Map<String, String> oauth2) { this.oauth2 = oauth2; } }
2. 编写多租户Security配置类
直接通过@Bean方法批量生成每个租户的独立SecurityFilterChain,无需自定义factoryMethod:
@Configuration @EnableWebSecurity @EnableConfigurationProperties(TenantSecurityProperties.class) public class MultiTenantSecurityConfig { private final TenantSecurityProperties tenantProps; private final ClientRegistrationRepository clientRegistrationRepo; private final OAuth2AuthorizedClientService authorizedClientService; // 构造注入依赖 public MultiTenantSecurityConfig(TenantSecurityProperties tenantProps, ClientRegistrationRepository clientRegistrationRepo, OAuth2AuthorizedClientService authorizedClientService) { this.tenantProps = tenantProps; this.clientRegistrationRepo = clientRegistrationRepo; this.authorizedClientService = authorizedClientService; } @Bean public List<SecurityFilterChain> tenantSecurityFilterChains(HttpSecurity http) throws Exception { List<SecurityFilterChain> filterChains = new ArrayList<>(); int chainOrder = 1; // 优先级:数字越小,匹配优先级越高 // 遍历每个租户,生成对应的SecurityFilterChain for (Map.Entry<String, String> tenantEntry : tenantProps.getOauth2().entrySet()) { String tenantName = tenantEntry.getKey(); String issuerUri = tenantEntry.getValue(); // 复制HttpSecurity实例,避免多租户配置互相污染 HttpSecurity tenantHttp = http.copyWithDefaults(); filterChains.add( tenantHttp // 设置根路径匹配器:仅处理该租户的路径请求 .securityMatcher("/" + tenantName + "/**") // 配置授权规则 .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) // 配置OAuth2登录(根据需求可选) .oauth2Login(oauth2 -> oauth2 .clientRegistrationRepository(clientRegistrationRepo) .authorizedClientService(authorizedClientService) .authorizationEndpoint(endpoint -> endpoint .baseUri("/" + tenantName + "/oauth2/authorization")) .redirectionEndpoint(endpoint -> endpoint .baseUri("/" + tenantName + "/login/oauth2/code/*"))) // 配置OAuth2资源服务器(JWT模式) .oauth2ResourceServer(rs -> rs.jwt(jwt -> jwt.issuerUri(issuerUri))) // 设置会话策略(按需配置) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 设置当前链的优先级 .order(chainOrder++) .build() ); } // 兜底规则:处理非租户路径(按需配置,比如直接拒绝访问) filterChains.add( http .securityMatcher("/**") .authorizeHttpRequests(auth -> auth.anyRequest().denyAll()) .order(Integer.MAX_VALUE) .build() ); return filterChains; } }
关键细节说明
- 避免配置污染:使用
HttpSecurity.copyWithDefaults()创建独立的HttpSecurity实例,每个租户的配置都是隔离的,不会出现复用实例导致的匹配器层级问题,所有配置的securityMatcher都是根级别,仅匹配对应租户的路径。 - 自动注册Bean:Spring会自动将
@Bean方法返回的List<SecurityFilterChain>中的每个元素注册为独立的Bean,完全符合Spring Security的运行机制——框架会自动扫描所有SecurityFilterChainBean,按order排序后匹配请求。 - 优先级控制:通过
order()方法设置每个租户链的优先级,确保租户路径优先被对应的规则处理,兜底链设置最高的Integer.MAX_VALUE,最后执行。
内容的提问来源于stack exchange,提问作者Cezary
相关产品推荐
相关产品推荐

