2013-02-27 9 views
9

Ich versuche, meine Open-Source-Projekt Method-Level-Sicherheit mit Anmerkungen und Feder-Sicherheit hinzuzufügen. Das Problem, dem ich jetzt gegenüberstehe, sind findAll-Methoden, insbesondere die für Paging (zB das Zurücksenden einer Seite).Spring Data JPA und Feder-Sicherheit: Filter auf Datenbank-Ebene (speziell für Paging)

Verwenden von @PostFilter funktioniert auf Listen (aber ich persönlich glaube, es ist keine gute Idee, in der Anwendung und nicht Datenbank zu filtern), aber vollständig bei Paging-Abfragen fehlschlägt.

Das ist problematisch, weil ich eine Entität habe, die List<Compound> enthält. Es gibt verschiedene Implementierungen von Verbindungen, und ein Benutzer hat möglicherweise nur das Recht, eine der Verbindungen zu lesen. Compound verwendet TABLE_PER_CLASS Vererbung. Repositorys implementieren QueryDslPredicateExecutor.

Ich denke, dass jeder Abfrage ein Prädikat hinzugefügt werden muss, das die Rückgabeergebnisse basierend auf dem aktuellen Benutzer einschränkt. Allerdings bin ich etwas verloren auf a) wie das Datenmodell für Benutzer und Rollen aussehen sollte und b) wie man dann das Prädikat erstellt (dies ist wahrscheinlich einfach, sobald das Modell definiert ist). Oder bietet querydsl bereits eine typbasierte Filterung (auf Elementen in der abgefragten Klasse)?

+0

Für Frage A siehe [User Schema] (http://static.springsource.org/spring-security/site/docs/3.2.x/reference/springsecurity-single.html#d0e8380]) Abschnitt –

+0

Ich meinte es so dass ich Abfragen anpassen kann, um die Rollen zu berücksichtigen, die die aktuellen Benutzer haben, z. Es muss eine Beziehung von einer gegebenen Entität zu einer Rolle und von einer Rolle zu einem Benutzer bestehen. –

+0

Dann werfen Sie einen Blick in die ACL http://static.springsource.org/spring-security/site/docs/3.2.x/reference/springsecurity-single.html#domain-acls und das entsprechende DB-Schema http: // static. springource.org/spring-security/site/docs/3.2.x/reference/springsecurity-single.html#dbschema-acl –

Antwort

2

Vorläufig kam a folgende Lösung. Da mein Projekt ziemlich einfach ist, funktioniert das vielleicht nicht für ein komplexeres Projekt.

  1. kann entweder ein Benutzer alle oder keine der Einheiten einer bestimmten Klasse lesen

daher jede Abfrage Methode kann mit @PreAuthorize enthält hasRole mit Anmerkungen versehen werden.

Die Ausnahme ist die Container Einheit in meinem Projekt. Es kann jede Unterklasse von Compound enthalten und ein Benutzer hat möglicherweise nicht die Berechtigung, alle von ihnen anzuzeigen. Sie müssen Filter sein.

Dafür habe ich eine User und Role Einheit erstellt. Compound hat eine OneToOne-Beziehung zu Role und die Rolle ist die "read_role" für diese Compound. User und Role haben eine ManyToMany-Beziehung.

@Entity 
public abstract class Compound {  
    //... 
    @OneToOne  
    private Role readRole; 
    //... 
} 

Alle meine Repositories implementieren QueryDSLPredicateExecutor und das wird sehr Hand hier. Anstatt benutzerdefinierte findBy-Methoden im Repository zu erstellen, erstellen wir sie nur in der Serviceebene und verwenden repositry.findAll(predicate) und repository.findOne(predicate). Das Prädikat enthält die eigentliche Benutzereingabe + den "Sicherheitsfilter".

@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; 
} 

Hinweis: QCompoundContainer ist die "Meta-Modell" Klasse von QueryDSL erzeugt.

Endlich müssen Sie wahrscheinlich die QueryDSL Weg von Container zu User initialisieren:

@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; 
    //... 
} 

Weglassen dieser letzte Schritt zu einem führen NullPointerException kann.

Weitere Tipp: CompoundService stellt automatisch Rolle sparen:

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) 

Dies funktioniert. Der Nachteil ist ein wenig offensichtlich. Das gleiche Role ist mit jeder einzelnen Instanz der gleichen Compound Klassenimplementierung verbunden.

6

Derzeit gibt es keine solche Unterstützung, aber wir haben es auf der Roadmap. Sie können für den allgemeinen Fortschritt DATACMNS-293 folgen.