diff --git a/core/src/main/java/org/springframework/security/authentication/CachingUserDetailsService.java b/core/src/main/java/org/springframework/security/authentication/CachingUserDetailsService.java index 39dc1f64877..da6e7de77d1 100644 --- a/core/src/main/java/org/springframework/security/authentication/CachingUserDetailsService.java +++ b/core/src/main/java/org/springframework/security/authentication/CachingUserDetailsService.java @@ -23,6 +23,55 @@ import org.springframework.util.Assert; /** + * Implementation of {@link UserDetailsService} that utilizes caching through a + * {@link UserCache} + *

+ * If a null {@link UserDetails} instance is got from calling + * {@link UserCache#getUserFromCache(String)} to the {@link UserCache} got from + * {@link #getUserCache()}, the user load is deferred to the {@link UserDetailsService} + * provided during construction. Otherwise, the instance got from cache is returned. + *

+ * It is initialized with a {@link NullUserCache} by default, so it's strongly recommended + * setting your own {@link UserCache} using {@link #setUserCache(UserCache)}, otherwise, + * the delegate will be called every time. + *

+ * Utilize this class by defining {@link org.springframework.context.annotation.Bean} that + * encapsulates an actual implementation of {@link UserDetailsService} and set an + * {@link UserCache}. + *

+ * For example:
{@code
+ * @Bean
+ * public CachingUserDetailsService cachingUserDetailsService(UserDetailsService delegate,
+ *                                                            UserCache userCache) {
+ *     CachingUserDetailsService service = new CachingUserDetailsService(delegate);
+ *     service.setUserCache(userCache);
+ *     return service;
+ * }
+ * }
+ * + *

+ * However, a preferable approach would be to use + * {@link org.springframework.cache.annotation.Cacheable} in your + * {@link UserDetailsService#loadUserByUsername(String)} implementation to cache + * {@link UserDetails} by username, reducing boilerplate and setup, specially + * if you are already using cache in your application. + *

+ * + * For example: + * + *
{@code
+ * @Service
+ * public class MyCustomUserDetailsImplementation implements UserDetailsService {
+
+ *     @Override
+ *     @Cacheable
+ *     public UserDetails loadUserByUsername(String username) {
+ *         //some logic here to get the actual user details
+ *         return userDetails;
+ *     }
+ * }
+ * }
+ * * @author Luke Taylor * @since 2.0 */ diff --git a/docs/modules/ROOT/pages/servlet/authentication/passwords/cached.adoc b/docs/modules/ROOT/pages/servlet/authentication/passwords/cached.adoc new file mode 100644 index 00000000000..8d58252b6ee --- /dev/null +++ b/docs/modules/ROOT/pages/servlet/authentication/passwords/cached.adoc @@ -0,0 +1,38 @@ +[[servlet-authentication-cached]] += CachingUserDetailsService + +Spring Security's `CachingUserDetailsService` implements xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService] offering support for caching authentication. + +`CachingUserDetailsService` provides caching support for `UserDetails` by delegating the authentication process to the provided `UserDetailsService`. The result is then stored in a `UserCache` to reduce computation in subsequent calls. + +Utilize this class by defining a `@Bean` of it that encapsulates a concrete implementation of `UserDetailsService` and set a `UserCache` to cache authenticated `UserDetails`. + +For example: + +[source,java] +---- +@Bean +public CachingUserDetailsService cachingUserDetailsService(UserDetailsService delegate, UserCache userCache) { + CachingUserDetailsService service = new CachingUserDetailsService(delegate); + service.setUserCache(userCache); + return service; +} +---- + +However, a preferable approach would be to use `@Cacheable` in your `UserDetailsService.loadUserByUsername(String)` implementation to cache `UserDetails` by `username`, reducing boilerplate and setup, especially if you are already using cache in your application. + +For example: + +[source,java] +---- +@Service +public class MyCustomUserDetailsImplementation implements UserDetailsService { + + @Override + @Cacheable + public UserDetails loadUserByUsername(String username) { + // some logic here to get the actual user details + return userDetails; + } +} +---- diff --git a/docs/modules/ROOT/pages/servlet/authentication/passwords/user-details-service.adoc b/docs/modules/ROOT/pages/servlet/authentication/passwords/user-details-service.adoc index 0e7b89c9509..d3b3ab914ff 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/passwords/user-details-service.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/passwords/user-details-service.adoc @@ -2,7 +2,7 @@ = UserDetailsService {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. -Spring Security provides xref:servlet/authentication/passwords/in-memory.adoc#servlet-authentication-inmemory[in-memory] and xref:servlet/authentication/passwords/jdbc.adoc#servlet-authentication-jdbc[JDBC] implementations of `UserDetailsService`. +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`. You can define custom authentication by exposing a custom `UserDetailsService` as a bean. For example, the following listing customizes authentication, assuming that `CustomUserDetailsService` implements `UserDetailsService`: