注册后自动登录 Spring Boot 3/Spring security 6

Lac*_*lev 5 spring spring-security spring-boot

我的问题主要针对 Spring Boot 3.x/Spring Security 6.x,但也可能适用于旧版本。我有一个带有表单登录的小项目。这是一个示例配置。

@Configuration
public class SecurityConfig {

  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.
        ...
        formLogin().
        loginPage("/users/login").
        ...
    return http.build();
  }

  @Bean
  public UserDetailsService userDetailsService(UserRepository userRepository) {
    return new AppUserDetailsService(userRepository);
  }

  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }
}
Run Code Online (Sandbox Code Playgroud)

确实没什么特别的,而且工作得很好。我还有一张登记表。它也工作正常,并且用户已在数据库中正确创建。之后就可以登录了。到目前为止,一切都很好。

我想做的最后一件事是在注册后立即自动登录用户。在早期的 Srping 版本中,这使用了类似的代码,可能是受到这里的启发:

@Override
  public void createAccount(UserRegistrationDTO userRegistrationDTO) {

    UserEntity userEntity = new UserEntity();
    userEntity.
        setFirstName(userRegistrationDTO.getFirstName()).
        setLastName(userRegistrationDTO.getLastName()).
        setEmail(userRegistrationDTO.getEmail()).
        setPassword(passwordEncoder.encode(userRegistrationDTO.getPassword()));
    userRepository.save(userEntity);

    var userDetails = userDetailsService.loadUserByUsername(userRegistrationDTO.getEmail());
    Authentication authentication = new UsernamePasswordAuthenticationToken(
      userDetails,
      userDetails.getPassword(),
      userDetails.getAuthorities()
    );

    SecurityContextHolder.
        getContext().
        setAuthentication(authentication);
  }
Run Code Online (Sandbox Code Playgroud)

这在 3.x/6.x 中不再有效

我的猜测是我需要使用AuthenticationManager,但我不确定如何注入正确的本地AuthenticationManager以及哪个安全过滤器链。我什至不知道如何使用AuthenticationManagerResolver<HttpServletRequest>如果这是解决它的方法......

我已经成功地通过在 Spring 上下文中使用HttpServletRequest.login(userName, password)委托来实现该功能,并且可能还执行数百万其他操作。AuthenticationManager但我有一种感觉,我正在做一些非常错误的事情。

实现这个简单功能的正确方法是什么?

Lac*_*lev 7

首先,非常感谢 Marcus Hert da Coregio 为我指明了正确的方向。事实上,这是这里描述的 Spring security 6 中的一个重大变化。安全上下文存储库不再是HttpSessionSecurityContextRepository,而是DelegatingSecurityContextRepository此处对此进行了描述。

我所做的是将其公开DelegatingSecurityContextRepository为 bean,如迁移文档中所暗示的:

  @Bean
  public SecurityContextRepository securityContextRepository() {
    return new DelegatingSecurityContextRepository(
        new RequestAttributeSecurityContextRepository(),
        new HttpSessionSecurityContextRepository()
    );
  }
Run Code Online (Sandbox Code Playgroud)

后来我的安全配置以类似的方式扩展:

  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http, 
        SecurityContextRepository securityContextRepository) throws Exception {
    
    http.
       ...
       .and().
        securityContext().
          securityContextRepository(securityContextRepository);

    return http.build();
  }
Run Code Online (Sandbox Code Playgroud)

自动登录已修复,如下所示(重要的一行是将上下文保存在存储库中):

@Override
  public void createAccount(UserRegistrationDTO userRegistrationDTO,
      HttpServletRequest request,
      HttpServletResponse response) {

    ...
   
    SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
        .getContextHolderStrategy();

    SecurityContext context = securityContextHolderStrategy.createEmptyContext();
    context.setAuthentication(authentication);
    securityContextHolderStrategy.setContext(context);

    securityContextRepository.saveContext(context, request, response);
  }

Run Code Online (Sandbox Code Playgroud)


Mar*_*gio 5

如果它在迁移到 Spring Security 6 后停止工作,则可能与需要显式保存SecurityContext.

这意味着您应该通过调用显式保存上下文SecurityContextRepository

SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
securityContextRepository.saveContext(request, response, context);
Run Code Online (Sandbox Code Playgroud)

securityContextRepository通常在哪里HttpSessionSecurityContextRepository