2013-02-27 12 views
9

Próbuję dodać zabezpieczenia poziomu metod do mojego projektu open source przy użyciu adnotacji i zabezpieczeń sprężynowych. Problem, z którym teraz borykam się, to metody FindAll, szczególnie te dla stronicowania (np. Zwrócenie strony).Spring Data JPA and spring-security: filtruj na poziomie bazy danych (szczególnie w przypadku stronicowania)

Używanie @PostFilter działa na listach (ale osobiście uważam, że nie jest dobrym pomysłem filtrowanie w aplikacji, a nie w bazie danych), ale całkowicie zawiedzie w przypadku zapytań stronicowania.

Jest to problematyczne, ponieważ mam jednostkę zawierającą List<Compound>. Istnieją różne implementacje złożenia, a użytkownik może mieć tylko przywilej czytania jednego ze związków. Związek używa dziedziczenia TABLE_PER_CLASS. Repozytoria implementują QueryDslPredicateExecutor.

Moim celem jest dodanie predykatu do każdego zapytania, które ogranicza wyniki zwracane na podstawie bieżącego użytkownika. Jednak jestem trochę zagubiony na a) sposób, w jaki model danych dla użytkownika i ról powinien wyglądać i b) jak utworzyć predykat (prawdopodobnie jest to łatwe, gdy model zostanie zdefiniowany). Czy też querydsl oferuje już filtrowanie oparte na typie (na elementach zawartych w kwerendach)?

+0

Dla pytania A patrz [Schemat użytkownika] (http://static.springsource.org/spring-security/site/docs/3.2.x/reference/springsecurity-single.html#d0e8380]) sekcja –

+0

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. –

+0

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 –

Odpowiedz

2

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.

  1. 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.

6

Obecnie nie ma takiego wsparcia, ale mamy je na mapie drogowej. Możesz śledzić DATACMNS-293 dla ogólnego postępu.

Powiązane problemy