You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
156 lines
4.8 KiB
156 lines
4.8 KiB
[[servlet-authentication-caching-user-details]] |
|
= Caching `UserDetails` |
|
|
|
Spring Security provides support for caching `UserDetails` with <<servlet-authentication-caching-user-details-service,`CachingUserDetailsService`>>. |
|
Alternatively, you can use Spring Framework's <<servlet-authentication-caching-user-details-cacheable,`@Cacheable`>> annotation. |
|
In either case, you will need to <<servlet-authentication-caching-user-details-credential-erasure,disable credential erasure>> in order to validate passwords retrieved from the cache. |
|
|
|
[[servlet-authentication-caching-user-details-service]] |
|
== `CachingUserDetailsService` |
|
|
|
Spring Security's `CachingUserDetailsService` implements xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[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` |
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,role="primary"] |
|
---- |
|
@Bean |
|
public CachingUserDetailsService cachingUserDetailsService(UserCache userCache) { |
|
UserDetailsService delegate = ...; |
|
CachingUserDetailsService service = new CachingUserDetailsService(delegate); |
|
service.setUserCache(userCache); |
|
return service; |
|
} |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,role="secondary"] |
|
---- |
|
@Bean |
|
fun cachingUserDetailsService(userCache: UserCache): CachingUserDetailsService { |
|
val delegate: UserDetailsService = ... |
|
val service = CachingUserDetailsService(delegate) |
|
service.userCache = userCache |
|
return service |
|
} |
|
---- |
|
====== |
|
|
|
[[servlet-authentication-caching-user-details-cacheable]] |
|
== `@Cacheable` |
|
|
|
An alternative approach would be to use Spring Framework's {spring-framework-reference-url}integration.html#cache-annotations-cacheable[`@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` |
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,role="primary"] |
|
---- |
|
@Service |
|
public class MyCustomUserDetailsImplementation implements UserDetailsService { |
|
|
|
@Override |
|
@Cacheable |
|
public UserDetails loadUserByUsername(String username) { |
|
// some logic here to get the actual user details |
|
return userDetails; |
|
} |
|
} |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,role="secondary"] |
|
---- |
|
@Service |
|
class MyCustomUserDetailsImplementation : UserDetailsService { |
|
|
|
@Cacheable |
|
override fun loadUserByUsername(username: String): UserDetails { |
|
// some logic here to get the actual user details |
|
return userDetails |
|
} |
|
} |
|
---- |
|
====== |
|
|
|
[[servlet-authentication-caching-user-details-credential-erasure]] |
|
== Disable Credential Erasure |
|
|
|
Whether you use <<servlet-authentication-caching-user-details-service,`CachingUserDetailsService`>> or <<servlet-authentication-caching-user-details-cacheable,`@Cacheable`>>, you will need to disable xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager-erasing-credentials[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` |
|
[tabs] |
|
===== |
|
Java:: |
|
+ |
|
[source,java,role="primary"] |
|
---- |
|
@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); |
|
} |
|
|
|
} |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,role="secondary"] |
|
---- |
|
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) |
|
} |
|
|
|
} |
|
---- |
|
=====
|
|
|