JDBC 会话和 Spring Security 的 Spring Boot 3 迁移不起作用

She*_*men 2 spring spring-security spring-boot spring-session

这是 Spring Boot 3.0.1 版本的问题。这对于 2.7.5 版本来说工作得非常好。

我的应用程序使用 Spring Security 和 JDBC 会话。所以我被X-Auth-Token保存在spring_session我的数据库(PostgreSQL)的表中。当我调用登录控制器方法时,我已经实现了UserDetailsService从数据库中获取用户的接口。

当迁移到新的 Spring Boot 3 版本时,主要的变化发生在WebSecurityConfig类中。以前它是从WebSecurityConfigurerAdapter班级延伸出来的。所以我知道问题出在这堂课的某个地方。

在这里,我复制并粘贴该类的旧版本 2.7.5(工作正常)和新的 3.0.1 版本WebSecurityConfig

Spring引导版本2.7.5

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Autowired
    UserDetailsService userDetailsService;

    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {        
        http
            .csrf().disable()
            .exceptionHandling()
                .authenticationEntryPoint(restAuthenticationEntryPoint)
                .and()
            .cors()
                .and()
            .httpBasic()
                .and()
            .authorizeRequests()
                .antMatchers("/api/usermanager/auth/login").permitAll()
                .antMatchers(HttpMethod.GET, "/api/usermanager/image/org/**").permitAll()
                .antMatchers("/invalidSession*").anonymous()              
                .anyRequest().authenticated()
                .and()
            .formLogin()              
                .permitAll()
                .failureHandler(authenticationFailureHandler)               
                .and()
            .sessionManagement()
                .sessionFixation()
                .migrateSession()               
                .maximumSessions(1)
                .expiredUrl("/sessionExpired.html")
                .maxSessionsPreventsLogin(false);
    }

    @Override
    public void configure(WebSecurity web) {
        web
            .ignoring()
                .antMatchers("/docs/**", "/resources/**", "/static/**");
    }

    @Bean
    public AuthenticationFailureHandler myFailureHandler() {
        return new CustomAuthenticationFailureHandler();
    }

    @Bean
    public HttpSessionIdResolver httpSessionStrategy() {
        return HeaderHttpSessionIdResolver.xAuthToken();
    }

    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth
            .inMemoryAuthentication()
                .withUser("client")
                    .password(passwordEncoder.encode("Client@Venturi"))
                    .roles("USER");      
        var daoAC = new DaoAuthenticationConfigurer(userDetailsService);
        daoAC.passwordEncoder(passwordEncoder);
        auth.apply(daoAC);
    }
}
Run Code Online (Sandbox Code Playgroud)

Spring引导版本3.0.1

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig {

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity http,
                                                       PasswordEncoder passwordEncoder)
            throws Exception {
        var daoAC = new DaoAuthenticationConfigurer(userDetailsService);
        daoAC.passwordEncoder(passwordEncoder);

        var builder = http.getSharedObject(AuthenticationManagerBuilder.class);
        builder.apply(daoAC);
        return builder.build();    
    }

    private static final String[] AUTH_WHITELIST = {
            "/api/usermanager/auth/login"           
    };

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {       
        http
            .csrf().disable()
            .exceptionHandling()              
                .and()
            .cors()
                .and()
            .httpBasic()
                .and()
            .authorizeHttpRequests(
                        requests -> requests.
                        requestMatchers(AUTH_WHITELIST).permitAll()
                                .anyRequest().authenticated()
                )
            .sessionManagement()
                .sessionFixation()
                .migrateSession()               
                .maximumSessions(1)
                .expiredUrl("/sessionExpired.html")
                .maxSessionsPreventsLogin(false);

        return http.build();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().requestMatchers(HttpMethod.GET,
                "/docs/**", "/resources/**", "/static/**"
        );
    }

    @Bean
    public AuthenticationFailureHandler myFailureHandler() {
        return new CustomAuthenticationFailureHandler();
    }

    @Bean
    public HttpSessionIdResolver httpSessionStrategy() {
        return HeaderHttpSessionIdResolver.xAuthToken();
    }

    @Bean
    public HttpSessionIdResolver httpSessionIdResolver() {
        return HeaderHttpSessionIdResolver.xAuthToken();
    }

    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }
}
Run Code Online (Sandbox Code Playgroud)

当我调用上面的身份验证/登录控制器 API 时,我得到表中填充的会话 ID spring_session。但principal_name该表中的 是null。我希望您知道该spring_session表是Spring Session创建的默认表,用于保存会话信息。

在2.7.5版本中,principal_name将保存(用户登录名)。x-auth-token所以在后续的请求中,我可以通过在HTTP请求头中传递 来获取用户。所以我现在的问题是,当我调用上面的控制器 API 时,它principal_name总是null在表中。spring_session

WebSecurityConfig谁能看看我在将类转换为 Spring Boot 版本 3时是否做错了什么?

小智 5

与以前版本的差异是由于此更改:https ://github.com/spring-projects/spring-security/issues/11110

您可以使用旧策略(已弃用)自动保存 SecurityContextHolder 设置:

http.securityContext((securityContext) -> securityContext.requireExplicitSave(false))
Run Code Online (Sandbox Code Playgroud)