为了账号安全,请及时绑定邮箱和手机立即绑定

模块化 Spring Security SecurityWebFilterChain

模块化 Spring Security SecurityWebFilterChain

眼眸繁星 2023-02-16 16:47:00
我们的 Spring Security 配置文件越来越大,我们想将它分解成更小的部分。现在我们有以下内容: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();我们想用它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();    }我们希望端点的所有其他配置都是隐式的。Spring Security 怎么可能做这样的模块化呢?
查看完整描述

1 回答

?
慕容3067478

TA贡献1773条经验 获得超3个赞

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


    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所以它注入所有类型的beanHttpSecurityConfig并应用配置,就像这样:


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;

}


查看完整回答
反对 回复 2023-02-16
  • 1 回答
  • 0 关注
  • 148 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信