如何在 Spring Security 6 中为自定义 UsernamePasswordAuthenticationFilter 正确设置 SecurityContextRepository?

Sle*_*vin 8 spring spring-security spring-boot

从 Spring Security 6.0 开始,需要显式保存 SecurityContextRepository: https ://docs.spring.io/spring-security/reference/migration/servlet/session-management.html#_require_explicit_ saving_of_securitycontextrepository

这意味着,如果使用自定义 UsernamePasswordAuthenticationFilter,则必须提供 SecurityContextRepository 才能持久保存 SecurityContext,如下所示:

@Component
public class InternalUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    @PostConstruct
    private void setup() {
        super.setSecurityContextRepository(
                new DelegatingSecurityContextRepository(
                        new RequestAttributeSecurityContextRepository(),
                        new HttpSessionSecurityContextRepository()));
    }
    //...
}
Run Code Online (Sandbox Code Playgroud)

尽管此处示例中的 @Max 也在 SecurityFilterChain 中设置了存储库,但在 AuthenticationFilter 中仅指定一次 SecurityContextRepository 似乎就足够了。

我已经测试了这两种变体,并且是否在 FilterChain 中指定了 SecurityContextRepository 似乎并不重要。如果我们为 AuthenticationFilter 和 FilterChain 使用一个 bean,或者为每个 bean 创建新的存储库,似乎也并不重要,如下所示:

@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true)
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain mvcFilterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(authorize -> authorize
                        .shouldFilterAllDispatcherTypes(true)
                        .dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
                        .requestMatchers("/", "/login/**").permitAll()
                        .anyRequest().authenticated())

                .addFilterAt(internalUsernamePasswordAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)

                .securityContext(securityContext -> securityContext
                        .securityContextRepository(
                                new DelegatingSecurityContextRepository(
                                        new RequestAttributeSecurityContextRepository(),
                                        new HttpSessionSecurityContextRepository()
                                )))
                //...
    }
}
Run Code Online (Sandbox Code Playgroud)

这表明仅使用 AuthenticationFilter 的存储库,而忽略 FilterChain 的存储库。

因此,我的假设是在 AuthenticationFilter 中创建 SecurityContextRepository 就足够了。

所以我的问题是:

  1. 我看对了吗?
  2. 或者我应该定义一个 bean?
  3. 是否有理由在 SecurityFilterChain 中设置此 bean?

非常感谢您的澄清帮助

dek*_*ard 3

像您这样的 - 派生过滤器SecurityContextRepository中使用的和是不同的。AbstractAuthenticationProcessingFilterInternalUsernamePasswordAuthenticationFilterSecurityFilterChain

显然,过滤器SecurityContextRepository专门使用文件管理器来在成功身份验证后保存安全上下文。
如果您没有明确指定存储库,它将创建并使用RequestAttributeSecurityContextRepository.

另一方面,您传递给安全链构建器的 会在多个地方使用,例如在创建过程中SecurityContextRepository传递给by 、 to by等 。如果您没有显式指定存储库,将为这些组件创建if会话创建策略是无状态的,否则。SessionManagementFilterSessionManagementConfigurerLogoutFilterLogoutConfigurer
SessionManagementConfigurernew RequestAttributeSecurityContextRepository()new DelegatingSecurityContextRepository(httpSecurityRepository, new RequestAttributeSecurityContextRepository())

因此,这些是独立的扩展点,您可以根据需要使用它们。否则SecurityContextRepository将使用默认实现。

定义SecurityContextRepositorybean 不会做任何事情,因为我提到的组件不会查找它的应用程序上下文。