0

У меня есть веб-аппликация, использующая весенний ботинок, весеннюю безопасность и весенние данные. он без гражданства.SpringCacheBasedUserCache is null

Я хотел бы избежать, чтобы всегда коллировать db для пользователя. Поэтому я думаю, используя 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 и другой, который реализует UserDetailsService

@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 является нулевым

+1

Ваш 'UserServiceImpl' не является управляемым компонентом, поэтому ничего не будет введено. Рядом с этим ваш код испорчен. «Пользователь», который вы кладете в кеш, всегда будет «null». –

+1

Вместо того, чтобы выполнять кэширование самостоятельно, просто заверните свой собственный 'UserDetailsService' в' CachingUserDetailsService', который делает тяжелую работу для вас. –

ответ

2

Либо ставить @Bean по методу userDetailsServiceBean или (как это было предложено) удалить кэширование с вашего 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): Конструктор не является публичным, что затрудняет создание компонента. Этого можно достичь, используя BeanUtils и ClassUtils. Замените вызов на new следующим образом, чтобы создать экземпляр.

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

Edit (2): Видимо, я уже столкнулся с этим уже однажды (около 2 лет назад) и зарегистрирован this issue для него.

+0

CachingUserDetailsService (UserDetailsService) не является общедоступным –

+0

Darn пропустил, что конструктор не был 'public'. Странно, что с xml его довольно легко настроить, но для конфигурации на основе java, по-видимому, его можно настроить (легко) для 'jdbcAuthentication'. Вы можете обойти это, используя «BeanUtils» с «ClassUtils» для создания экземпляра. (См. Измененный ответ). –

+0

ok и если пользователь меняет свой пароль, мне нужно удалить пользователя кеша? –