2016-11-23 163 views
0

我有使用spring boot,spring security和spring data的web applicatoin。它是无状态的。SpringCacheBasedUserCache为null

我想避免总是调用数据库用户访问。所以我想使用SpringCacheBasedUserCache。

@Configuration 
@EnableCaching 
public class CacheConfig { 

    @Bean 
    CacheManager cacheManager() { 
     SimpleCacheManager cacheManager = new SimpleCacheManager(); 
     cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("city"), new ConcurrentMapCache("userCache"))); 
     return cacheManager; 
    } 

    @Bean 
    public UserCache userCache() throws Exception { 

     Cache cache = (Cache) cacheManager().getCache("userCache"); 
     return new SpringCacheBasedUserCache(cache); 
    } 
} 


@EnableCaching 
@Configuration 
@EnableWebSecurity 
public class ApplicationSecurity extends WebSecurityConfigurerAdapter { 
    @Bean 
    public PasswordEncoder passwordEncoder() { 
     return new BCryptPasswordEncoder(); 
    } 

    @Override 
    public UserDetailsService userDetailsServiceBean() throws Exception { 
     return new UserServiceImpl(commerceReposiotry, repository, defaultConfigRepository); 
    } 
    ... 
} 

我有谁实现的UserDetails类和另一谁实现的UserDetailsS​​ervice

@Service 
public class UserServiceImpl implements UserDetailsService, UserService { 

    private final CommerceRepository commerceReposiotry; 
    private final UserAppRepository repository; 
    private final DefaultConfigRepository defaultConfigRepository; 

    @Autowired 
    private UserCache userCache; 

    @Autowired 
    private PasswordEncoder passwordEncoder; 

    @Autowired 
    public UserServiceImpl(final CommerceRepository commerceReposiotry, final UserAppRepository repository, final DefaultConfigRepository defaultConfigRepository) { 
     this.commerceReposiotry = commerceReposiotry; 
     this.repository = repository; 
     this.defaultConfigRepository = defaultConfigRepository; 
    } 


    @Override 
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 

     UserDetails user = userCache.getUserFromCache(username); 
     UserApp userapp = null; 

     if (user == null) { 
      userapp = repository.findByUsername(username); 
     } 

     if (userapp == null) { 
      throw new UsernameNotFoundException("Username " + username + " not found"); 
     } 

     userCache.putUserInCache(user); 

     return new CustomUserDetails(userapp); 
    } 
    ... 
} 

在loadUserByUsername方法,userCache为null

+1

你的'UserServiceImpl'不是一个弹簧托管bean,所以什么都不会被注入。在那之前你的代码是有缺陷的。放入缓存的'user'将始终为'null'。 –

+1

而不是自己缓存,只需将自己的'UserDetailsS​​ervice'包装在一个'CachingUserDetailsS​​ervice'中,它为您完成繁重的工作。 –

回答

2

要么把@BeanuserDetailsServiceBean方法或(的建议)删除从UserDetailsService中完全缓存并将其包装在CachingUserDetailsService中,而改为简单地覆盖userDetailsService方法。

@Configuration 
@EnableWebSecurity 
public class ApplicationSecurity extends WebSecurityConfigurerAdapter { 

    @Autowired 
    private UserCache userCache; 

    @Bean 
    public PasswordEncoder passwordEncoder() { 
     return new BCryptPasswordEncoder(); 
    } 

    @Override 
    public UserDetailsService userDetailsService() throws Exception { 

     UserServiceImpl userService = new UserServiceImpl(commerceReposiotry, repository, defaultConfigRepository); 
     CachingUserDetailsService cachingUserService = new CachingUserDetailsService(userService); 
     cachingUserService.setUserCache(this.userCache); 
     return cachingUserService; 
    } 
    ... 
} 

您的其他配置已经有@EnableCaching,因此不需要重新配置。只需将缓存注入配置类并构建一个CachingUserDetailsService,它代表您的UserDetailsService来检索用户。

当然,您将不得不从您自己的UserDetailsService中删除缓存,现在可以专注于用户管理/检索,而不是混合缓存。

编辑(1):构造函数没有公开使得它更难创建一个bean。这可以使用BeanUtilsClassUtils来实现。用下面的代码替换new的呼叫应该创建一个实例。

private UserDetailsService cachingUserDetailsService(UserDetailsService delegate) { 
    Constructor<CachingUserDetailsService> ctor = ClassUtils.getConstructorIfAvailable(CachingUserDetailsService.class, UserDetailsService.class); 
    return BeanUtils.instantiateClass(ctor, delegate); 
} 

编辑(2):显然我已经遇到了这个已经一次(约2年前),并为其注册this issue

+0

CachingUserDetailsS​​ervice(UserDetailsS​​ervice)未公开 –

+0

Darn错过构造函数不是'public'。奇怪的是,使用xml很容易配置,但对于基于java的配置,显然它只能被配置(很容易)用于'jdbcAuthentication'。你可以通过使用'BeanUtils'和'ClassUtils'来创建一个实例来解决这个问题。 (请参阅修改后的答案)。 –

+0

好吧,如果用户修改密码,我需要删除缓存的用户? –