mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-28 13:18:45 +00:00 
			
		
		
		
	
		
			
	
	
		
			157 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			157 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|  | [[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) | ||
|  | 	} | ||
|  | 
 | ||
|  | } | ||
|  | ---- | ||
|  | ===== |