如何解决使用Springboot缓存UserDetails
我正在尝试在我的应用程序中实现UserCache
,以避免在我使用基本身份验证的情况下多次调用User表。我根据此topic的可接受答案创建了CacheConfig
,其中CachingUserDetailsService
用于管理用户缓存。波纹管是UserService
,CacheConfig
和SecurityConfig
的代码:
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
public UserService(UserRepository repository) {
this.userRepository = repository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
AddInUser user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("O usuário " + username + " não pode ser enconTrado!"));
UserDetails userDetails = User
.builder()
.username(user.getUsername())
.password(user.getpassword())
.roles("USER")
.build();
return userDetails;
}
@Transactional
public AddInUser save(AddInUser user) {
return userRepository.save(user);
}
}
@EnableCaching
@Configuration
public class CacheConfig {
public static final String USER_CACHE = "userCache";
/**
* Define cache strategy
*
* @return CacheManager
*/
@Bean
public CacheManager cacheManager() {
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
List<Cache> caches = new ArrayList<>();
//Failure after 5 minutes of caching
caches.add(new GuavaCache(CacheConfig.USER_CACHE,CacheBuilder.newBuilder().expireAfteraccess(5,TimeUnit.MINUTES).build()));
simpleCacheManager.setCaches(caches);
return simpleCacheManager;
}
@Bean
public UserCache userCache() throws Exception {
Cache cache = cacheManager().getCache("userCache");
return new SpringCacheBasedUserCache(cache);
}
}
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
};
@Autowired
private UserRepository userRepository;
@Autowired
private UserCache userCache;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())
.and()
.authorizeRequests()
.antMatchers("/api/**")
.authenticated()
.and()
.httpBasic();
}
@Override
@Bean
public UserDetailsService userDetailsService() {
UserService userService = new UserService(userRepository);
CachingUserDetailsService cachingUserService = new CachingUserDetailsService(userService);
cachingUserService.setUserCache(this.userCache);
return cachingUserService;
}
}
第一个调用工作良好,因为它可以调用UserRepository
。但是第二,它没有调用存储库(如预期的那样),但是我从BCryptPasswordEncoder
得到以下警告:
2020-09-24 08:43:51.327 WARN 24624 --- [nio-8081-exec-4] o.s.s.c.bcrypt.BCryptPasswordEncoder : Empty encoded password
该警告的含义很清楚,并且由于null
密码而无法验证用户身份。但是我不明白为什么从缓存中重试的用户如果密码正确存储,密码为空。我不确定如何使用缓存解决问题。有什么想法吗?
非常感谢您的帮助!
解决方法
@ M.Deinum评论是绝对正确的。您可以参考文档here。
请注意,此实现不是一成不变的。它实现了 CredentialsContainer界面,以允许输入密码 认证后擦除。如果您这样做可能会产生副作用 将实例存储在内存中并重用它们。如果是这样,请确保您 每次调用UserDetailsService时都会返回一个副本。
如果您想了解更多信息,可以查看Spring-security source code:
private boolean eraseCredentialsAfterAuthentication = true;
...
if (result != null) {
if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {
// Authentication is complete. Remove credentials and other secret data
// from authentication
((CredentialsContainer) result).eraseCredentials();
}
// If the parent AuthenticationManager was attempted and successful then it
// will publish an AuthenticationSuccessEvent
// This check prevents a duplicate AuthenticationSuccessEvent if the parent
// AuthenticationManager already published it
if (parentResult == null) {
this.eventPublisher.publishAuthenticationSuccess(result);
}
return result;
}
还有User.java源代码:
@Override
public void eraseCredentials() {
this.password = null;
}
顺便说一句,以这种方式缓存登录用户看起来很奇怪。登录期间,最好从数据库而不是从缓存中获取新记录。您可以在其他地方使用缓存的用户,但是很少在登录时看到它。
如果确实需要这样做,可以按照doc中所述将默认标志更改为false
,只需注入AuthenticationManager
并调用:
setEraseCredentialsAfterAuthentication(false)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。