Na razie wpadliśmy na następujące rozwiązanie. Ponieważ mój projekt jest dość prosty, może nie działać w przypadku bardziej złożonego projektu.
- użytkownik może albo czytać wszystkie albo żaden z podmiotów, o pewnej klasie
stąd każda metoda kwerendy mogą być opatrzone @PreAuthorize
zawierający hasRole
.
Wyjątkiem jest jednostka Container
w moim projekcie. Może zawierać dowolną podklasę o numerze Compound
, a użytkownik może nie mieć uprawnień do przeglądania ich wszystkich. Muszą być filtrowane.
W tym celu utworzyłem jednostkę User
i Role
. Compound
ma związek OneToOne z Role
i ta rola to "read_role" dla tego Compound
. User
i Role
mają związek ManyToMany.
@Entity
public abstract class Compound {
//...
@OneToOne
private Role readRole;
//...
}
Wszystkie moje repozytorium implementują QueryDSLPredicateExecutor
i to staje się bardzo dobrze tutaj. Zamiast tworzyć niestandardowe metody findBy w repozytorium, tworzymy je tylko w warstwie usług i używamy repositry.findAll(predicate)
i repository.findOne(predicate)
. Predykat zawiera rzeczywiste dane wejściowe użytkownika + "filtr bezpieczeństwa".
@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;
}
Uwaga: QCompoundContainer
jest klasa "meta-modelu" generowane przez QueryDSL.
W końcu prawdopodobnie trzeba zainicjować ścieżkę QueryDSL z Container
do 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;
//...
}
Pomijając ten ostatni krok może prowadzić do NullPointerException
.
Dalsze podpowiedź: CompoundService
automatycznie ustawia rolę na zapisać:
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)
To działa. Wadą jest trochę oczywiste. Ten sam numer Role
jest powiązany z każdą pojedynczą instancją tej samej implementacji klasy Compound
.
Dla pytania A patrz [Schemat użytkownika] (http://static.springsource.org/spring-security/site/docs/3.2.x/reference/springsecurity-single.html#d0e8380]) sekcja –
Miałem na myśli że mogę dostosować zapytania, aby uwzględnić role obecnych użytkowników, np. musi istnieć relacja z danego podmiotu do roli i od roli do użytkownika. –
Następnie zajrzyj do list ACL http://static.springsource.org/spring-security/site/docs/3.2.x/reference/springsecurity-single.html#domain-acls i odpowiedniego schematu DB http: // static. springsource.org/spring-security/site/docs/3.2.x/reference/springsecurity-single.html#dbschema-acl –