问题
Faced a problem with caching during authorization, I can't figure it out. So that during authorization every time a request is not made to the database, I want to cache the method. At the first request with an empty cache, everything is fine and until the cache is deleted after a time. But while the cache is there, I get 401, because o.s.s.c.bcrypt.BCryptPasswordEncoder: Empty encoded password, the password is empty every time. How to be in such a situation?
@Override
@Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.debug("Load user {}", username);
return userService.findUserDetailsByName(username).orElseThrow(() -> new UsernameNotFoundException(username));
}
@Cacheable(cacheNames = Caches.USER_DETAILS_CACHE)
public Optional<UserDetails> findUserDetailsByName(String username) {
Optional<UserDetails> userDetailsOpt =
userRepository.findOne(QUser.user.userName.eq(username)).map(UserService::createFrom);
return userDetailsOpt.map(u -> withUserDetails(u).build());
}
@Configuration(proxyBeanMethods = false)
@EnableCaching
@RequiredArgsConstructor
public class CacheConfiguration {
@Bean
public CacheManager cacheManager(Ticker ticker, @Value("${app.cache.user.ttl}") Duration userTtl) {
SimpleCacheManager cacheManager = new SimpleCacheManager();
List<CaffeineCache> caches = new ArrayList<>();
caches.add(buildCache(Caches.USER_DETAILS_CACHE, ticker, userTtl, 1));
cacheManager.setCaches(caches);
return cacheManager;
}
@Bean
public Ticker ticker() {
return Ticker.systemTicker();
}
private static CaffeineCache buildCache(String name, Ticker ticker, Duration ttl, int size) {
return new CaffeineCache(name, Caffeine.newBuilder()
.expireAfterWrite(ttl)
.ticker(ticker)
.maximumSize(size)
.build());
}
}
回答1:
Assuming you are using the default Spring Security User as the UserDetails implementation. This implements CredentialsContainer which will have its password cleared after use for security reasons.
The caching is done for the object instance instead of raw data and thus it will be cleared in the cache as well.
If you are using JPA you better use the 2nd level caching integration of your JPA provider instead of external caching.
回答2:
I solved the problem this way
private final Cache userDetailsCache;
public UserService(CacheManager cacheManager) {
this.userDetailsCache = cacheManager.getCache(Caches.USER_DETAILS_CACHE);
}
public Optional<UserDetails> findUserDetailsByName(String username) {
ValueWrapper value = userDetailsCache.get(username);
if (value == null) {
Optional<UserDetails> userDetailsOpt =
userRepository.findOne(QUser.user.userName.eq(username)).map(UserService::createFrom);
userDetailsOpt.ifPresent(u -> userDetailsCache.put(username, u));
return userDetailsOpt.map(u -> withUserDetails(u).build());
} else {
return Optional.ofNullable((UserDetails) value.get()).map(u -> withUserDetails(u).build());
}
}
来源:https://stackoverflow.com/questions/65177102/empty-encoded-password-when-using-cacheable-in-userdetailsservice