2016-11-23 165 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

好吧,如果用戶修改密碼,我需要刪除緩存的用戶? –