Caching UserDetails

Spring Security provides support for caching UserDetails with CachingUserDetailsService. Alternatively, you can use Spring Framework’s @Cacheable annotation. In either case, you will need to disable credential erasure in order to validate passwords retrieved from the cache.

CachingUserDetailsService

Spring Security’s CachingUserDetailsService implements UserDetailsService to provide support for caching UserDetails. CachingUserDetailsService provides caching support for UserDetails by delegating to the provided UserDetailsService. The result is then stored in a UserCache to reduce computation in subsequent calls.

The following example simply defines a @Bean that encapsulates a concrete implementation of UserDetailsService and a UserCache for caching the UserDetails:

Provide a CachingUserDetailsService @Bean
  • Java

  • Kotlin

@Bean
public CachingUserDetailsService cachingUserDetailsService(UserCache userCache) {
	UserDetailsService delegate = ...;
    CachingUserDetailsService service = new CachingUserDetailsService(delegate);
    service.setUserCache(userCache);
    return service;
}
@Bean
fun cachingUserDetailsService(userCache: UserCache): CachingUserDetailsService {
    val delegate: UserDetailsService = ...
    val service = CachingUserDetailsService(delegate)
    service.userCache = userCache
    return service
}

@Cacheable

An alternative approach would be to use Spring Framework’s @Cacheable in your UserDetailsService implementation to cache UserDetails by username. The benefit to this approach is simpler configuration, especially if you are already using caching elsewhere in your application.

The following example assumes caching is already configured, and annotates the loadUserByUsername with @Cacheable:

UserDetailsService annotated with @Cacheable
  • Java

  • Kotlin

@Service
public class MyCustomUserDetailsImplementation implements UserDetailsService {

    @Override
    @Cacheable
    public UserDetails loadUserByUsername(String username) {
        // some logic here to get the actual user details
        return userDetails;
    }
}
@Service
class MyCustomUserDetailsImplementation : UserDetailsService {

    @Cacheable
    override fun loadUserByUsername(username: String): UserDetails {
        // some logic here to get the actual user details
        return userDetails
    }
}

Disable Credential Erasure

Whether you use CachingUserDetailsService or @Cacheable, you will need to disable credential erasure so that the UserDetails will contain a password to be validated when retrieved from the cache. The following example disables credential erasure for the global AuthenticationManager by configuring the AuthenticationManagerBuilder provided by Spring Security:

Disable credential erasure for the global AuthenticationManager
  • Java

  • Kotlin

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		// ...
		return http.build();
	}

	@Bean
	public UserDetailsService userDetailsService() {
		// Return a UserDetailsService that caches users
		// ...
	}

	@Autowired
	public void configure(AuthenticationManagerBuilder builder) {
		builder.eraseCredentials(false);
	}

}
import org.springframework.security.config.annotation.web.invoke

@Configuration
@EnableWebSecurity
class SecurityConfig {

	@Bean
	fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
		// ...
		return http.build()
	}

	@Bean
	fun userDetailsService(): UserDetailsService {
		// Return a UserDetailsService that caches users
		// ...
	}

	@Autowired
	fun configure(builder: AuthenticationManagerBuilder) {
		builder.eraseCredentials(false)
	}

}