Spring Boot 如何在Sping Boot 上下文中覆盖SecurityFilterChain?

k2arahey  于 2023-02-19  发布在  Spring
关注(0)|答案(1)|浏览(223)

我面临的问题是,仅仅通过阅读文档并不能明显解决。在迁移到Sping Boot v2.7.4 / Spring Security v5.7.3时,我重构了配置,不扩展WebSecurityConfigurerAdapter,如下所示:

@Configuration
@EnableWebSecurity
public class CustomSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.
            csrf().disable().
            logout().disable().
            authorizeRequests().anyRequest().permitAll();

        return http.build();
    }
}

调用了上述方法,但是没有任何效果,因为使用了由OAuth2SecurityFilterChainConfiguration创建的SecurityFilterChain示例(我通过检查堆栈中的过滤器列表看到了这一点,例如LogoutFilter,它应该被上述配置禁用)。调试日志:

2022-10-20 15:49:48.790 [main] o.s.b.a.s.DefaultWebSecurityCondition    : Condition DefaultWebSecurityCondition on org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerJwtConfiguration$OAuth2SecurityFilterChainConfiguration matched due to AllNestedConditions 2 matched 0 did not; NestedCondition on DefaultWebSecurityCondition.Beans @ConditionalOnMissingBean (types: org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter,org.springframework.security.web.SecurityFilterChain; SearchStrategy: all) did not find any beans; NestedCondition on DefaultWebSecurityCondition.Classes @ConditionalOnClass found required classes 'org.springframework.security.web.SecurityFilterChain', 'org.springframework.security.config.annotation.web.builders.HttpSecurity'
2022-10-20 15:49:48.791 [main] a.ConfigurationClassBeanDefinitionReader : Registered bean definition for imported class 'org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerJwtConfiguration$OAuth2SecurityFilterChainConfiguration'
2022-10-20 15:49:48.792 [main] o.s.b.a.condition.OnBeanCondition        : Condition OnBeanCondition on org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerJwtConfiguration$OAuth2SecurityFilterChainConfiguration#jwtSecurityFilterChain matched due to @ConditionalOnBean (types: org.springframework.security.oauth2.jwt.JwtDecoder; SearchStrategy: all) found bean 'jwtDecoderByJwkKeySetUri'
...
2022-10-20 15:49:49.082 [main] a.ConfigurationClassBeanDefinitionReader : Registering bean definition for @Bean method com.mycompany.CustomSecurityConfig.filterChain()
...
2022-10-20 15:49:52.276 [main] edFilterInvocationSecurityMetadataSource : Adding web access control expression [authenticated] for any request
2022-10-20 15:50:13.348 [main] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@33502cfe, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@729d1428, org.springframework.security.web.context.SecurityContextPersistenceFilter@7d0312a, org.springframework.security.web.header.HeaderWriterFilter@6ca97ddf, org.springframework.security.web.csrf.CsrfFilter@38f569d, org.springframework.security.web.authentication.logout.LogoutFilter@1104ad6a, org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter@74ab8610, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@7833407, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@66acaa54, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@115924ba, org.springframework.security.web.session.SessionManagementFilter@6a905513, org.springframework.security.web.access.ExceptionTranslationFilter@5749e633, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@49741e80]
...
2022-10-20 15:50:13.384 [main] edFilterInvocationSecurityMetadataSource : Adding web access control expression [permitAll] for any request
2022-10-20 15:50:17.641 [main] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@4a0f4282, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@19d3f4fb, org.springframework.security.web.context.SecurityContextPersistenceFilter@99f75e4, org.springframework.security.web.header.HeaderWriterFilter@118c1faa, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@2b6ff016, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5aefdb9e, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@43cf97a8, org.springframework.security.web.session.SessionManagementFilter@da5b46f, org.springframework.security.web.access.ExceptionTranslationFilter@11267e87, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@7827cdfc]

是否预期bean CustomSecurityConfig.filterChain参与DefaultWebSecurityCondition评估,并且不创建OAuth2SecurityFilterChainConfiguration.jwtSecurityFilterChain。或者DefaultWebSecurityCondition的问题是WebSecurityConfigurerAdapter的示例不再位于上下文中(至于issue #10822,它已被弃用)?
添加@Order()注解的建议不起作用:

@Configuration
@EnableWebSecurity
public class CustomSecurityConfig {

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { ...

以及进一步尝试排除自动配置类,如下所示:

@SpringBootApplication(excludeName = "org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerJwtConfiguration.OAuth2SecurityFilterChainConfiguration")
public class Application extends SpringBootServletInitializer { ...

失败可能是由于issue #5427,并出现以下错误

java.lang.IllegalStateException: The following classes could not be excluded because they are not auto-configuration classes:
    - org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerJwtConfiguration.OAuth2SecurityFilterChainConfiguration
    at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.handleInvalidExcludes(AutoConfigurationImportSelector.java:222) ~[spring-boot-autoconfigure-2.7.4.jar!/:2.7.4]

这个办法也行不通:

@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*OAuth2ResourceServerJwtConfiguration.*")})
public class Application extends SpringBootServletInitializer { ...

发布前阅读的文档:

我已经创建了一个small Maven project来演示这个问题。在项目启动后,像这样请求控制器:

$ wget -nv -O - 'http://localhost:8080/spring/test'
Username/Password Authentication Failed.

可以看到,自定义配置的SecurityFilterChain不是活动的,因为否则访问将被授予(对于antMatchers( "/**/test").permitAll())。ContextRefreshedEvent侦听器转储两个SecurityFilterChain示例(jwtSecurityFilterChainfilterChain),它们的优先级不可能可靠地配置。

qco9c6ql

qco9c6ql1#

如问题#33103所示,通过@ImportResource从XML导入的bean不参与Sping Boot 自动配置,因此应使用注解执行bean扫描,即@SpringBootApplication(scanBasePackages =“some.package”)-这基本上解决了问题。- dma_k 2022年11月17日9:21
感谢此回复,我找到了解决方案。但由于我们正在使用不同的身份验证方法,所以它可能不适合您。我正在使用这个tutorial中的DaoAuthenticationProvider。然后不使用“WebSecurityConfigurerAdapter”重写为新版本。
希望这个解决方案有帮助。我被折磨了3-4个小时才找到解决方案。
1.我创建了一个名为“config”的子包,它与“models、controller、service等”(或者随便你给它们起什么名字)处于同一级别。x1c 0d1x
1.然后,通过“scanBasePackages”手动导入此包以及其他包。

@SpringBootApplication(scanBasePackages = {"com.example.test.controller", "com.example.test.model", "com.example.test.repo", "com.example.test.service","com.example.test.config"})
public class TestApplication {
    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }

}

这里有3个重写的文件在我的“配置”文件夹中。都是从上面提到的教程中重写的。

  • a.自定义用户详细信息 *
package com.example.test.config;

import java.util.Collection;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.example.test.model.UserModel;

public class CustomUserDetails implements UserDetails {

    //@Autowired no need cuz this is not a bean
    private UserModel user;

    public CustomUserDetails(UserModel user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getEmail();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    public String getFullName() {
        return user.getFirstName() + " " + user.getLastName();
    }

}
  • B.自定义用户详细信息服务 *
package com.example.test.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import com.example.test.repo.UserRepo;
import com.example.test.model.UserModel;
import org.springframework.stereotype.Service;

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepo userRepo;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserModel user = userRepo.findByEmail(username);
        if (user == null) {
            throw new UsernameNotFoundException("User not found");
        }
        return new CustomUserDetails(user);
    }

}
  • c.安全配置 *
package com.example.test.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfiguration{

    @Bean
    public UserDetailsService userDetailsService() {
        return new CustomUserDetailsService();
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService());
        authProvider.setPasswordEncoder(passwordEncoder());

        return authProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(
            AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests ((requests)->requests
                .requestMatchers("/", "/register", "/try2Register").permitAll()
                .anyRequest().authenticated()
                )
                .formLogin((form)->form
                    .usernameParameter("email")
                    .defaultSuccessUrl("/users")
                    .permitAll()
                )
                .logout((logout) -> logout
                    .logoutSuccessUrl("/").permitAll());
        http.authenticationProvider(authenticationProvider());
        return http.build();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().requestMatchers("/images/**", "/js/**", "/webjars/**");
    }
}

相关问题