【问题标题】:Migrating from WebSecurityConfigurerAdapter to SecurityFilterChain从 WebSecurityConfigurerAdapter 迁移到 SecurityFilterChain
【发布时间】:2022-08-12 00:02:04
【问题描述】:

这是我迁移前的工作安全配置:


    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .antMatchers(\"/auth/**\")
                .antMatchers(\"/swagger-ui/**\")
                .antMatchers(\"/swagger-ui.html\")
                .antMatchers(\"/swagger-resources/**\")
                .antMatchers(\"/v2/api-docs/**\")
                .antMatchers(\"/v3/api-docs/**\");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedPortalRoleConverter);

        http
                .csrf().disable()
                .cors()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(new AuthenticationFallbackEntryPoint())
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests(authorize -> authorize.anyRequest().authenticated())
                .oauth2ResourceServer()
                .jwt().jwtAuthenticationConverter(jwtAuthenticationConverter);
    }

这是我迁移后的安全链配置:

    @Bean
    @Order(1)
    public SecurityFilterChain ignorePathsSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(authorize -> authorize
                        .antMatchers(
                                \"/auth/**\",
                                \"/swagger-ui/**\",
                                \"/swagger-ui.html\",
                                \"/swagger-resources/**\",
                                \"/v3/api-docs/**\")
                            .permitAll());

        return http.build();
    }

    @Bean
    @Order(2)   
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, GrantedPortalRoleConverter grantedPortalRoleConverter) throws Exception {
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedPortalRoleConverter);

        http
                .csrf().disable()
                .cors(Customizer.withDefaults())
                .exceptionHandling(configurer -> configurer.authenticationEntryPoint(new AuthenticationFallbackEntryPoint()))
                .sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
                .oauth2ResourceServer(configurer -> configurer.jwt().jwtAuthenticationConverter(jwtAuthenticationConverter));

        return http.build();
    }

使用原始配置,当我调用随机不存在的路径时:

    @Test
    void should_not_authenticate_or_return_not_found() throws Exception {
        logger.info(\"should_not_authenticate_or_return_not_found\");
        
        mvc.perform(get(\"/toto/tata\"))
                .andExpect(status().isUnauthorized());      
    }

我得到:

15:44:00.230 [main] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Failed to authorize filter invocation [GET /toto/tata] with attributes [authenticated]

使用新的 conf,我只是得到 HTTP 404,请问我在这里缺少什么?我看不到任何差异,调试日志也没有显示太多。

这是使用非工作 conf 丢失的第一行日志:

16:24:58.651 [main] DEBUG o.s.s.w.a.e.ExpressionBasedFilterInvocationSecurityMetadataSource - Adding web access control expression [authenticated] for any request

但是在两个日志中,我都可以看到(因为有 2 个安全链,所以新 conf 有 2 行):

o.s.s.web.DefaultSecurityFilterChain - Will secure any request with (...)

    标签: spring spring-security


    【解决方案1】:

    解释

    当你有多个SecurityFilterChains时,你必须指定一个请求匹配器,否则所有请求都会被第一个SecurityFilterChain处理,注解@Order(1),永远不会到达第二个SecurityFilterChain,注解@Order(2) .

    在您上面分享的代码中,这意味着在ignorePathsSecurityFilterChain 中配置.requestMatchers()

    @Bean
    @Order(1)
    public SecurityFilterChain ignorePathsSecurityFilterChain(HttpSecurity http) throws Exception {
        http
            .requestMatchers(requests -> requests // add this block
                .antMatchers(
                    "/auth/**",
                    "/swagger-ui/**",
                    "/swagger-ui.html",
                    "/swagger-resources/**",
                    "/v3/api-docs/**")
            )
            .authorizeHttpRequests(authorize -> authorize
                .antMatchers(
                    "/auth/**",
                    "/swagger-ui/**",
                    "/swagger-ui.html",
                    "/swagger-resources/**",
                    "/v3/api-docs/**")
                .permitAll());
    
        return http.build();
    }
    

    这意味着只有与/auth/**/swagger-ui/** 等匹配的请求将由ignorePathsSecurityFilterChain 处理,而其余请求将转到defaultSecurityFilterChain

    要了解requestMatchersauthorizeHttpRequests 之间的区别,您可以查看this StackOverflow question

    解决方案

    一个更好的选择是将SecurityFilterChains 组合成一个。在这种情况下,我看不出有任何理由将它们分开。

    生成的配置将是:

    @Bean
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, GrantedPortalRoleConverter grantedPortalRoleConverter) throws Exception {
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedPortalRoleConverter);
    
        http
                .authorizeHttpRequests(authorize -> authorize
                        .antMatchers(
                                "/auth/**",
                                "/swagger-ui/**",
                                "/swagger-ui.html",
                                "/swagger-resources/**",
                                "/v3/api-docs/**")
                        .permitAll()
                        .anyRequest().authenticated()
                )
                .csrf().disable()
                .cors(Customizer.withDefaults())
                .exceptionHandling(configurer -> configurer.authenticationEntryPoint(new AuthenticationFallbackEntryPoint()))
                .sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .oauth2ResourceServer(configurer -> configurer.jwt().jwtAuthenticationConverter(jwtAuthenticationConverter));
    
        return http.build();
    }
    

    选择

    或者,您可以使用 WebSecurityCustomizer 忽略某些端点:

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().antMatchers(
                    "/auth/**",
                    "/swagger-ui/**",
                    "/swagger-ui.html",
                    "/swagger-resources/**",
                    "/v3/api-docs/**");
    }
    

    然后您将使用defaultSecurityFilterChain 作为您唯一的SecurityFilterChain

    【讨论】:

    • 感谢您的详细回答,确实我最终按照您的建议进行了组合。但我仍然不明白为什么 2 SecurityFilterChain 不能应用于同一个端点。也许它不是为此而设计的,但是为什么要接受在没有警告或错误的情况下开始呢?
    • 按请求顺序检查过滤器链。一旦一个匹配,它就不会寻找另一个,因为不清楚应该应用哪一个。请求仅通过一个过滤器链的原因有几个,其中之一是两个链中都有许多通用过滤器,如果多次应用过滤器,您会得到意外的行为。
    【解决方案2】:

    有没有办法将两个 SecurityFilterChain bean 与基本身份验证和 Oauth 结合为一个?

    【讨论】:

      猜你喜欢
      • 2022-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-04
      • 2011-04-26
      • 2015-07-23
      • 2020-05-31
      相关资源
      最近更新 更多