0

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);
kostepanych
  • 2,229
  • 9
  • 32
  • 47
  • 1
    I'm not sure that it is possible for in memory authentication. However all you would need is your own implementation of UserDetailsService which you would use in your config instead of the in memory authentication. There is a good article from [baeldung](https://www.baeldung.com/registration-with-spring-mvc-and-spring-security) regarding that + password encoding and user registration... which I used in the past – Johannes Apr 08 '22 at 14:20

2 Answers2

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;
}
Chaosfire
  • 4,818
  • 4
  • 8
  • 23
  • I get "Consider defining a bean of type 'org.springframework.security.core.userdetails.UserDetailsPasswordService' in your configuration." How to declare it in spring context? – kostepanych Apr 13 '22 at 16:53
  • @kostepanych Check the edit, i hope it will work. Strangely i couldn't replicate the error locally, with the same spring boot version and the same configuration, the bean gets created for me... – Chaosfire Apr 13 '22 at 19:56
0

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()));

Deb Das
  • 264
  • 1
  • 7