На данный момент придумал следующее решение. Поскольку мой проект довольно прост, это может не сработать для более сложного проекта.
- пользователь может либо прочитать все, либо ничего из сущностей определенного класса
следовательно, любой метод запроса может быть аннотирован с помощью @PreAuthorize
, содержащего hasRole
.
Исключением является объект Container
в моем проекте. Он может содержать любой подкласс Compound
, и пользователь может не иметь права просматривать их все. Они должны быть фильтрующими.
Для этого я создал сущности User
и Role
. Compound
имеет отношение OneToOne к Role
, и эта роль является "read_role" для этого Compound
. User
и Role
имеют отношение ManyToMany.
@Entity
public abstract class Compound {
//...
@OneToOne
private Role readRole;
//...
}
Все мои репозитории реализуют QueryDSLPredicateExecutor
, и здесь это очень удобно. Вместо создания пользовательских методов findBy в репозитории мы создаем их только на сервисном уровне и используем repositry.findAll(predicate)
и repository.findOne(predicate)
. Предикат содержит фактический пользовательский ввод + «фильтр безопасности».
@PreAuthorize("hasRole('read_Container'")
public T getById(Long id) {
Predicate predicate = QCompoundContainer.compoundContainer.id.eq(id);
predicate = addSecurityFilter(predicate);
T container = getRepository().findOne(predicate);
return container;
}
private Predicate addSecurityFilter(Predicate predicate){
String userName = SecurityContextHolder.getContext().getAuthentication().getName();
predicate = QCompoundContainer.compoundContainer.compound.readRole
.users.any().username.eq(userName).and(predicate);
return predicate;
}
Примечание. QCompoundContainer
— это класс «метамодели», созданный QueryDSL.
Наконец, вам, вероятно, нужно инициализировать путь QueryDSL с Container
на User
:
@Entity
public abstract class CompoundContainer<T extends Compound>
//...
@QueryInit("readRole.users") // INITIALIZE QUERY PATH
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL,
targetEntity=Compound.class)
private T compound;
//...
}
Пропуск этого последнего шага может привести к ошибке NullPointerException
.
Дополнительная подсказка: CompoundService
автоматически устанавливает роль при сохранении:
if (compound.getReadRole() == null) {
Role role = roleRepository.findByRoleName("read_" + getCompoundClassSimpleName());
if (role == null) {
role = new Role("read_" + getCompoundClassSimpleName());
role = roleRepository.save(role);
}
compound.setReadRole(role);
}
compound = getRepository().save(compound)
Это работает. Минус немного очевиден. Один и тот же Role
связан с каждым экземпляром одной и той же реализации класса Compound
.
person
beginner_
schedule
28.02.2013