Polish gh-14263
This commit is contained in:
parent
10e0f98d5e
commit
a32cd66179
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -26,51 +26,29 @@ import org.springframework.util.Assert;
|
|||
* Implementation of {@link UserDetailsService} that utilizes caching through a
|
||||
* {@link UserCache}
|
||||
* <p>
|
||||
* If a null {@link UserDetails} instance is got from calling
|
||||
* If a null {@link UserDetails} instance is returned from
|
||||
* {@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.
|
||||
* provided during construction. Otherwise, the instance retrieved from the cache is
|
||||
* returned.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* Utilize this class by defining {@link org.springframework.context.annotation.Bean} that
|
||||
* encapsulates an actual implementation of {@link UserDetailsService} and set an
|
||||
* {@link UserCache}.
|
||||
* Utilize this class by defining a {@link org.springframework.context.annotation.Bean}
|
||||
* that encapsulates an actual implementation of {@link UserDetailsService} and providing
|
||||
* a {@link UserCache} implementation.
|
||||
* </p>
|
||||
* For example: <pre>{@code
|
||||
* For example: <pre>
|
||||
* @Bean
|
||||
* public CachingUserDetailsService cachingUserDetailsService(UserDetailsService delegate,
|
||||
* UserCache userCache) {
|
||||
* public CachingUserDetailsService cachingUserDetailsService(UserCache userCache) {
|
||||
* UserDetailsService delegate = ...;
|
||||
* CachingUserDetailsService service = new CachingUserDetailsService(delegate);
|
||||
* service.setUserCache(userCache);
|
||||
* return service;
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* 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 <code>username</code>, reducing boilerplate and setup, specially
|
||||
* if you are already using cache in your application.
|
||||
* </p>
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* <pre>{@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;
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
* </pre>
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 2.0
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
[[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;
|
||||
}
|
||||
}
|
||||
----
|
|
@ -0,0 +1,156 @@
|
|||
[[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)
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
=====
|
|
@ -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], 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`.
|
||||
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`.
|
||||
|
||||
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`:
|
||||
|
|
Loading…
Reference in New Issue