'How to update the password in Spring SecurityContext?

Using Spring Boot 2.6.4. Here is my SecurityConfig class:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
  @Autowired
  SecurityService securityService;

  @Override
  protected void configure(HttpSecurity http) throws Exception
  {
    http.csrf().disable()
    .authorizeRequests().anyRequest().authenticated()
    .and()
    .httpBasic();
  }

  @Autowired
  public void configureGlobal(AuthenticationManagerBuilder auth)
  throws Exception
  {
    auth.inMemoryAuthentication()
    .withUser("admin")
    .password("{noop}"+securityService.getApiKey())
    .roles("ADMIN");
  }
 }

I'm entering here only when starting application. How to get to the configureGlobal method after changing the password?

Here is how I change my password in the @RestController class (just store it in DB):

@PostMapping
public void update(@Valid @RequestBody SecurityDto dto) {
    securityService.save(dto.getApiKey());
    SecurityContextHolder.clearContext();
  }

So my old password remains valid until restarting the application. Thats because I get to the SecurityConfig.configureGlobal method only when application starts. So how to change the password properly?

UPDATE: Resolved by implementing own UserDetailsService and using it instead of inMemoryAuthentication

auth.userDetailsService(userDetailsService);


Solution 1:[1]

InMemoryUserDetailsManager implements UserDetailsPasswordService and UserDetailsManager. You can use either to change the password:

For UserDetailsPasswordService you can do:

private final UserDetailsPasswordService passwordService;
private final PasswordEncoder passwordEncoder;

@Autowired
public MyController(UserDetailsPasswordService passwordService, PasswordEncoder passwordEncoder) {
    this.passwordService = passwordService;
    this.passwordEncoder = passwordEncoder;
}

@PostMapping
public void update(@Valid @RequestBody SecurityDto dto) {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    Object principal = authentication.getPrincipal();
    UserDetails userDetails;
    if (principal instanceof UserDetails) {
        userDetails = (UserDetails) principal;
    } else {
        userDetails = new User(authentication.getName(), "", authentication.getAuthorities());
    }
    String newPass = dto.getApiKey();
    String pass = this.passwordEncoder.encode(newPass);
    this.passwordService.updatePassword(userDetails, pass);

    securityService.save(dto.getApiKey());
    SecurityContextHolder.clearContext();
}

If you don't use password encoder, just remove it, and manually prepend the {noop} prefix to the password. The important method is updatePassword().

You can refer to this question for how to do it with UserDetailsManager. The method is changePassword?().

Edit: You can declare , and populate the bean with users manually in any @Configuration class:

@Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
  InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
  String password = "{noop}" + this.securityService.getApiKey();
  manager.createUser(new User("admin", password, Collections.singletonList(new SimpleGrantedAuthority("ADMIN"))));
  return manager;
}

Solution 2:[2]

You can create an interceptor for your http calls and then get the UserContext from there; usually the principal object will have all the headers in a map available which you can use to get the key which you are looking for authentication

//Create a USER object where your updating the specific attributes
    Optional<User> user = Optional.ofNullable(SecurityContextHolder.getContext())
        .map(SecurityContext::getAuthentication) // can override to update the authentication ideally change your password here
        .filter(Authentication::isAuthenticated)
        .map(Authentication::getPrincipal)
        .filter(DefaultOAuth2AuthenticatedPrincipal.class::isInstance)
        .map(DefaultOAuth2AuthenticatedPrincipal.class::cast)
        .map(principal -> getUser(principal.getAttributes()));

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Deb Das