Skip to content

Commit a32cd66

Browse files
committed
Polish gh-14263
1 parent 10e0f98 commit a32cd66

File tree

4 files changed

+168
-72
lines changed

4 files changed

+168
-72
lines changed

core/src/main/java/org/springframework/security/authentication/CachingUserDetailsService.java

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,51 +26,29 @@
2626
* Implementation of {@link UserDetailsService} that utilizes caching through a
2727
* {@link UserCache}
2828
* <p>
29-
* If a null {@link UserDetails} instance is got from calling
29+
* If a null {@link UserDetails} instance is returned from
3030
* {@link UserCache#getUserFromCache(String)} to the {@link UserCache} got from
3131
* {@link #getUserCache()}, the user load is deferred to the {@link UserDetailsService}
32-
* provided during construction. Otherwise, the instance got from cache is returned.
32+
* provided during construction. Otherwise, the instance retrieved from the cache is
33+
* returned.
3334
* <p>
3435
* It is initialized with a {@link NullUserCache} by default, so it's strongly recommended
3536
* setting your own {@link UserCache} using {@link #setUserCache(UserCache)}, otherwise,
3637
* the delegate will be called every time.
3738
* <p>
38-
* Utilize this class by defining {@link org.springframework.context.annotation.Bean} that
39-
* encapsulates an actual implementation of {@link UserDetailsService} and set an
40-
* {@link UserCache}.
39+
* Utilize this class by defining a {@link org.springframework.context.annotation.Bean}
40+
* that encapsulates an actual implementation of {@link UserDetailsService} and providing
41+
* a {@link UserCache} implementation.
4142
* </p>
42-
* For example: <pre>{@code
43+
* For example: <pre>
4344
* &#64;Bean
44-
* public CachingUserDetailsService cachingUserDetailsService(UserDetailsService delegate,
45-
* UserCache userCache) {
45+
* public CachingUserDetailsService cachingUserDetailsService(UserCache userCache) {
46+
* UserDetailsService delegate = ...;
4647
* CachingUserDetailsService service = new CachingUserDetailsService(delegate);
4748
* service.setUserCache(userCache);
4849
* return service;
4950
* }
50-
* }</pre>
51-
*
52-
* <p>
53-
* However, a preferable approach would be to use
54-
* {@link org.springframework.cache.annotation.Cacheable} in your
55-
* {@link UserDetailsService#loadUserByUsername(String)} implementation to cache
56-
* {@link UserDetails} by <code>username</code>, reducing boilerplate and setup, specially
57-
* if you are already using cache in your application.
58-
* </p>
59-
*
60-
* For example:
61-
*
62-
* <pre>{@code
63-
* &#64;Service
64-
* public class MyCustomUserDetailsImplementation implements UserDetailsService {
65-
66-
* &#64;Override
67-
* &#64;Cacheable
68-
* public UserDetails loadUserByUsername(String username) {
69-
* //some logic here to get the actual user details
70-
* return userDetails;
71-
* }
72-
* }
73-
* }</pre>
51+
* </pre>
7452
*
7553
* @author Luke Taylor
7654
* @since 2.0

docs/modules/ROOT/pages/servlet/authentication/passwords/cached.adoc

Lines changed: 0 additions & 38 deletions
This file was deleted.
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
[[servlet-authentication-caching-user-details]]
2+
= Caching `UserDetails`
3+
4+
Spring Security provides support for caching `UserDetails` with <<servlet-authentication-caching-user-details-service,`CachingUserDetailsService`>>.
5+
Alternatively, you can use Spring Framework's <<servlet-authentication-caching-user-details-cacheable,`@Cacheable`>> annotation.
6+
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.
7+
8+
[[servlet-authentication-caching-user-details-service]]
9+
== `CachingUserDetailsService`
10+
11+
Spring Security's `CachingUserDetailsService` implements xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService] to provide support for caching `UserDetails`.
12+
`CachingUserDetailsService` provides caching support for `UserDetails` by delegating to the provided `UserDetailsService`.
13+
The result is then stored in a `UserCache` to reduce computation in subsequent calls.
14+
15+
The following example simply defines a `@Bean` that encapsulates a concrete implementation of `UserDetailsService` and a `UserCache` for caching the `UserDetails`:
16+
17+
.Provide a `CachingUserDetailsService` `@Bean`
18+
[tabs]
19+
======
20+
Java::
21+
+
22+
[source,java,role="primary"]
23+
----
24+
@Bean
25+
public CachingUserDetailsService cachingUserDetailsService(UserCache userCache) {
26+
UserDetailsService delegate = ...;
27+
CachingUserDetailsService service = new CachingUserDetailsService(delegate);
28+
service.setUserCache(userCache);
29+
return service;
30+
}
31+
----
32+
33+
Kotlin::
34+
+
35+
[source,kotlin,role="secondary"]
36+
----
37+
@Bean
38+
fun cachingUserDetailsService(userCache: UserCache): CachingUserDetailsService {
39+
val delegate: UserDetailsService = ...
40+
val service = CachingUserDetailsService(delegate)
41+
service.userCache = userCache
42+
return service
43+
}
44+
----
45+
======
46+
47+
[[servlet-authentication-caching-user-details-cacheable]]
48+
== `@Cacheable`
49+
50+
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`.
51+
The benefit to this approach is simpler configuration, especially if you are already using caching elsewhere in your application.
52+
53+
The following example assumes caching is already configured, and annotates the `loadUserByUsername` with `@Cacheable`:
54+
55+
.`UserDetailsService` annotated with `@Cacheable`
56+
[tabs]
57+
======
58+
Java::
59+
+
60+
[source,java,role="primary"]
61+
----
62+
@Service
63+
public class MyCustomUserDetailsImplementation implements UserDetailsService {
64+
65+
@Override
66+
@Cacheable
67+
public UserDetails loadUserByUsername(String username) {
68+
// some logic here to get the actual user details
69+
return userDetails;
70+
}
71+
}
72+
----
73+
74+
Kotlin::
75+
+
76+
[source,kotlin,role="secondary"]
77+
----
78+
@Service
79+
class MyCustomUserDetailsImplementation : UserDetailsService {
80+
81+
@Cacheable
82+
override fun loadUserByUsername(username: String): UserDetails {
83+
// some logic here to get the actual user details
84+
return userDetails
85+
}
86+
}
87+
----
88+
======
89+
90+
[[servlet-authentication-caching-user-details-credential-erasure]]
91+
== Disable Credential Erasure
92+
93+
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.
94+
The following example disables credential erasure for the global `AuthenticationManager` by configuring the `AuthenticationManagerBuilder` provided by Spring Security:
95+
96+
.Disable credential erasure for the global `AuthenticationManager`
97+
[tabs]
98+
=====
99+
Java::
100+
+
101+
[source,java,role="primary"]
102+
----
103+
@Configuration
104+
@EnableWebSecurity
105+
public class SecurityConfig {
106+
107+
@Bean
108+
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
109+
// ...
110+
return http.build();
111+
}
112+
113+
@Bean
114+
public UserDetailsService userDetailsService() {
115+
// Return a UserDetailsService that caches users
116+
// ...
117+
}
118+
119+
@Autowired
120+
public void configure(AuthenticationManagerBuilder builder) {
121+
builder.eraseCredentials(false);
122+
}
123+
124+
}
125+
----
126+
127+
Kotlin::
128+
+
129+
[source,kotlin,role="secondary"]
130+
----
131+
import org.springframework.security.config.annotation.web.invoke
132+
133+
@Configuration
134+
@EnableWebSecurity
135+
class SecurityConfig {
136+
137+
@Bean
138+
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
139+
// ...
140+
return http.build()
141+
}
142+
143+
@Bean
144+
fun userDetailsService(): UserDetailsService {
145+
// Return a UserDetailsService that caches users
146+
// ...
147+
}
148+
149+
@Autowired
150+
fun configure(builder: AuthenticationManagerBuilder) {
151+
builder.eraseCredentials(false)
152+
}
153+
154+
}
155+
----
156+
=====

docs/modules/ROOT/pages/servlet/authentication/passwords/user-details-service.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
= UserDetailsService
33

44
{security-api-url}org/springframework/security/core/userdetails/UserDetailsService.html[`UserDetailsService`] is used by xref:servlet/authentication/passwords/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] for retrieving a username, a password, and other attributes for authenticating with a username and password.
5-
Spring Security provides xref:servlet/authentication/passwords/in-memory.adoc#servlet-authentication-inmemory[in-memory], xref:servlet/authentication/passwords/jdbc.adoc#servlet-authentication-jdbc[JDBC], and xref:servlet/authentication/passwords/cached.adoc#servlet-authentication-cached[in-cache] implementations of `UserDetailsService`.
5+
Spring Security provides xref:servlet/authentication/passwords/in-memory.adoc#servlet-authentication-inmemory[in-memory], xref:servlet/authentication/passwords/jdbc.adoc#servlet-authentication-jdbc[JDBC], and xref:servlet/authentication/passwords/caching.adoc#servlet-authentication-caching-user-details[caching] implementations of `UserDetailsService`.
66

77
You can define custom authentication by exposing a custom `UserDetailsService` as a bean.
88
For example, the following listing customizes authentication, assuming that `CustomUserDetailsService` implements `UserDetailsService`:

0 commit comments

Comments
 (0)