Использование Spring @Cacheable с @PostFilter - PullRequest
0 голосов
/ 28 октября 2019

Я пытаюсь использовать аннотации @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 после , когда фильтр запускается, вместо того, чтобы сохранять полный набор результатов на уровне репозитория, как я ожидаю? Или есть другой способ добиться желаемого поведения?

Бонусные баллы, если вы знаете, как я могу изящно добиться как кэширования, так и фильтрации одним и тем же методом в Сервисе. Я только создал избыточный репозиторий, потому что думал, что разделение методов решит проблему кеширования.

1 Ответ

0 голосов
/ 29 октября 2019

Оказывается, что содержимое кэшей Spring изменчиво, а аннотация @PostFilter изменяет возвращаемый список, но не фильтрует новый.

Таким образом, когда @PostFilter запускался после моего вызова метода Service выше, он фактически удалял элементы из списка, хранящегося в Cache, поэтому у второго запроса было только 1 результат, а у третьего - ноль.

Мое решение состояло в том, чтобы изменить Сервис на return new ArrayList<>(scopeRepo.getSegments());, чтобы PostFilter не изменял кэшированный список.

(ПРИМЕЧАНИЕ. Конечно, это не глубокий клон, поэтому, если кто-то изменил модель сегмента выше по потоку от службы, это, вероятно, также изменит модель в кэше. Так что это может быть не лучшим решением,но это работает для моего личного использования.)

Я не могу поверить, что Spring Cache являются изменяемыми ...

...