Я пытаюсь использовать аннотации @Cacheable
и @PostFilter
в Spring. Желаемое поведение заключается в том, что приложение будет кэшировать полный, нефильтрованный список сегментов (это очень маленький и очень часто упоминаемый список, поэтому производительность является желательным), но при этом пользователь будет иметь доступ только к определенным сегментам на основе своих ролей.
Я начал с @Cacheable
и @PostFilter
для одного метода, но когда это не сработало, я разбил их на два отдельных класса, чтобы иметь по одной аннотации для каждого метода. Тем не менее, похоже, что он ведет себя одинаково в любом случае, то есть когда пользователь А впервые обращается к сервису, когда он получает свой правильный отфильтрованный список, то, когда пользователь Б обращается к сервису в следующем, он не получает результатов, потому что кешхранит только отфильтрованные результаты пользователя А, а пользователь Б не имеет доступа ни к одному из них. (Таким образом, PostFilter все еще работает, но, похоже, в кэше хранится отфильтрованный список, а не полный список.)
Итак, вот соответствующий код:
конфигурация:
@Configuration
@EnableCaching
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class BcmsSecurityAutoConfiguration {
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(
new ConcurrentMapCache("bcmsSegRoles"),
new ConcurrentMapCache("bcmsSegments")
));
return cacheManager;
}
}
Служба:
@Service
public class ScopeService {
private final ScopeRepository scopeRepository;
public ScopeService(final ScopeRepository scopeRepository) {
this.scopeRepository = scopeRepository;
}
// Filters the list of segments based on User Roles. User will have 1 role for each segment they have access to, and then it's just a simple equality check between the role and the Segment model.
@PostFilter(value = "@bcmsSecurityService.canAccessSegment( principal, filterObject )")
public List<BusinessSegment> getSegments() {
List<BusinessSegment> segments = scopeRepository.getSegments();
return segments; // Debugging shows 4 results for User A (post-filtered to 1), and 1 result for User B (post-filtered to 0)
}
}
Репозиторий:
@Repository
public class ScopeRepository {
private final ScopeDao scopeDao; // This is a MyBatis interface.
public ScopeRepository(final ScopeDao scopeDao) {
this.scopeDao = scopeDao;
}
@Cacheable(value = "bcmsSegments")
public List<BusinessSegment> getSegments() {
List<BusinessSegment> segments = scopeDao.getSegments(); // Simple SELECT * FROM TABLE; Works as expected.
return segments; // Shows 4 results for User A, breakpoint not hit for User B cache takes over.
}
}
Кто-нибудь знает, почему появляется кэш? хранить результат метода Service после , когда фильтр запускается, вместо того, чтобы сохранять полный набор результатов на уровне репозитория, как я ожидаю? Или есть другой способ добиться желаемого поведения?
Бонусные баллы, если вы знаете, как я могу изящно добиться как кэширования, так и фильтрации одним и тем же методом в Сервисе. Я только создал избыточный репозиторий, потому что думал, что разделение методов решит проблему кеширования.