有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java模块化Spring安全性WebFilterChain

我们的Spring安全配置文件正在变得越来越大,我们希望将其分解为更小的部分。现在我们有以下几点:

public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
    http.securityMatcher(ServerWebExchangeMatchers.pathMatchers("/api/**"))
            .authenticationManager(this.authenticationManager);

    http.authorizeExchange()
            .pathMatchers(HttpMethod.GET, "/api/serviceA/**")
            .hasAuthority("PROP_A");

    http.authorizeExchange()
            .pathMatchers(HttpMethod.GET, "/api/serviceB/**")
            .hasAuthority("PROP_B");

    http.authorizeExchange().pathMatchers(HttpMethod.POST, "/api/login", "/api/logout", "/api/forgotPassword", "/api/confirmForgotPassword").permitAll();

    http.csrf()
            .disable()
            .formLogin()
            .authenticationEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED))
            .requiresAuthenticationMatcher(
                    ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, "/api/login"))
            .authenticationFailureHandler(CustomSpringSecurity::onAuthenticationFailure)
            .authenticationSuccessHandler(CustomSpringSecurity::onAuthenticationSuccess)
            .and()
            .logout()
            .logoutUrl("/api/logout")
            .logoutSuccessHandler(new CustomLogoutSuccessHandler(HttpStatus.OK));

    final SecurityWebFilterChain build = http.build();

    build
            .getWebFilters()
            .collectList()
            .subscribe(
                    webFilters -> {
                        for (WebFilter filter : webFilters) {
                            if (filter instanceof AuthenticationWebFilter) {
                                AuthenticationWebFilter awf = (AuthenticationWebFilter) filter;
                                awf.setServerAuthenticationConverter(CustomSpringSecurity::convert);
                            }
                        }
                    });

    return build;
}

我们想用securityMatcher来打破/api/seviceA/**/api/seviceB/**来拥有SecurityWebFilterChain @Beans

然而,我们的问题是配置中存在的额外配置数量。我们希望最终结果如下所示

    public SecurityWebFilterChain securityWebFilterChainForServiceA(ServerHttpSecurity http) {
        http.securityMatcher(ServerWebExchangeMatchers.pathMatchers("/api/serviceA/**"));

        http.authorizeExchange()
                .pathMatchers(HttpMethod.GET, "/api/serviceA/**")
                .hasAuthority("PROP_A");
        return http.build();
    }

我们希望端点的所有其他配置都是隐式的

在SpringSecurity中如何实现这样的模块化


共 (1) 个答案

  1. # 1 楼答案

    您可以这样指定一个接口:

        public interface HttpSecurityConfig {
            Consumer<ServerHttpSecurity> configuration();
        }
    

    然后为每个端点创建一个类来实现这一点,您可以将其作为bean注入:

        @Component
        public class ServiceASecurityConfig implements HttpSecurityConfig {
            @Override
            public Consumer<ServerHttpSecurity> configuration() {
                return (http) -> {
    
                    http.authorizeExchange()
                            .pathMatchers(HttpMethod.GET, "/api/serviceA/**")
                            .hasAuthority("PROP_A");
                };
            }
        }
    
        @Component
        public class ServiceBSecurityConfig implements HttpSecurityConfig {
            @Override
            public Consumer<ServerHttpSecurity> configuration() {
                return (http) -> {
    
                    http.authorizeExchange()
                            .pathMatchers(HttpMethod.GET, "/api/serviceB/**")
                            .hasAuthority("PROP_B");
                };
            }
        }
    
    

    最后修改SecurityWebFilterChain,这样它将注入HttpSecurityConfig类型的所有bean,并应用配置,如下所示:

    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http, final List<HttpSecurityConfig> httpConfigurations) {
        http.securityMatcher(ServerWebExchangeMatchers.pathMatchers("/api/**"))
                .authenticationManager(this.authenticationManager);
    
        // This line replaces the individual configurations in your original question
        httpConfigurations.forEach(config -> config.configuration().accept(http));
    
        http.authorizeExchange().pathMatchers(HttpMethod.POST, "/api/login", "/api/logout", "/api/forgotPassword", "/api/confirmForgotPassword").permitAll();
    
        http.csrf()
                .disable()
                .formLogin()
                .authenticationEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED))
                .requiresAuthenticationMatcher(
                        ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, "/api/login"))
                .authenticationFailureHandler(CustomSpringSecurity::onAuthenticationFailure)
                .authenticationSuccessHandler(CustomSpringSecurity::onAuthenticationSuccess)
                .and()
                .logout()
                .logoutUrl("/api/logout")
                .logoutSuccessHandler(new CustomLogoutSuccessHandler(HttpStatus.OK));
    
        final SecurityWebFilterChain build = http.build();
    
        build
                .getWebFilters()
                .collectList()
                .subscribe(
                        webFilters -> {
                            for (WebFilter filter : webFilters) {
                                if (filter instanceof AuthenticationWebFilter) {
                                    AuthenticationWebFilter awf = (AuthenticationWebFilter) filter;
                                    awf.setServerAuthenticationConverter(CustomSpringSecurity::convert);
                                }
                            }
                        });
    
        return build;
    }