Spring Security - @PreFilter e @PostFilter

1. Panoramica

In questo articolo impareremo come utilizzare le annotazioni @PreFilter e @PostFilter per proteggere le operazioni in un'applicazione Spring.

Se utilizzati insieme alle informazioni principali autenticate, @PreFilter e @PostFilter ci consentono di definire regole di sicurezza granulari utilizzando Spring Expression Language.

2. Presentazione di @PreFilter e @PostFilter

In poche parole, le annotazioni @PreFilter e @PostFilter vengono utilizzate per filtrare elenchi di oggetti in base alle regole di sicurezza personalizzate che definiamo.

@PostFilter definisce una regola per filtrare l'elenco restituito di un metodo, applicando tale regola a ogni elemento nell'elenco . Se il valore valutato è vero, l'elemento verrà mantenuto nell'elenco. In caso contrario, l'elemento verrà rimosso.

@PreFilter funziona in modo molto simile, tuttavia, il filtro viene applicato a un elenco che viene passato come parametro di input al metodo annotato.

Entrambe le annotazioni possono essere utilizzate su metodi o tipi (classi e interfacce). Li useremo solo sui metodi in questo articolo.

Tesi annotazioni non sono attive di default - avremo bisogno di consentire loro la @EnableGlobalMethodSecurity annotazione e prePostEnabled = true - nella nostra configurazione di sicurezza:

@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { // ... }

3. Scrittura delle regole di sicurezza

Per scrivere le regole di sicurezza in queste due annotazioni, utilizzeremo le espressioni Spring-EL; possiamo anche usare l'oggetto filterObject incorporato per ottenere un riferimento al particolare elemento della lista che viene testato.

Spring Security fornisce molti altri oggetti incorporati per creare regole molto specifiche ed esatte.

Ad esempio , possiamo usare @PreFilter per verificare se la proprietà assegnatario di un oggetto Task è uguale al nome dell'utente attualmente autenticato:

@PostFilter("filterObject.assignee == authentication.name") List findAll() { ... }

Abbiamo utilizzato l' annotazione @PostFilter qui poiché vogliamo che il metodo esegua e ottenga prima tutte le attività e passano ogni singola attività dall'elenco attraverso la nostra regola di filtro.

Quindi, se l'utente autenticato è michael , l'elenco finale delle attività restituite dal metodo findAll conterrebbe solo le attività assegnate a michael , anche se il database ha attività assegnate a jim e pam .

Ora rendiamo la regola un po 'più interessante. Supponiamo che se un utente è un manager, può vedere tutte le attività, indipendentemente da chi è assegnato:

@PostFilter("hasRole('MANAGER') or filterObject.assignee == authentication.name") List findAll() { // ... }

Abbiamo utilizzato il metodo integrato hasRole per verificare se l'utente autenticato ha il ruolo di MANAGER. Se hasRole restituisce true, l'attività verrà mantenuta nell'elenco finale. Quindi, se l'utente è un manager, la regola restituirà true per ogni elemento nell'elenco. Pertanto l'elenco finale conterrà tutti gli elementi.

Ora filtriamo un elenco passato come parametro a un metodo di salvataggio utilizzando @PreFilter :

@PreFilter("hasRole('MANAGER') or filterObject.assignee == authentication.name") Iterable save(Iterable entities) { // ... }

La regola di sicurezza è la stessa che abbiamo usato nell'esempio @PostFilter . La differenza principale qui è che gli elementi dell'elenco verranno filtrati prima dell'esecuzione del metodo, consentendoci così di rimuovere alcuni elementi dall'elenco, impedendo che vengano salvati nel database.

Quindi jim , che non è un manager, potrebbe provare a salvare un elenco di attività, alcune delle quali assegnate a pam . Tuttavia verranno incluse solo le attività assegnate a jim , le altre verranno ignorate.

4. Prestazioni su elenchi di grandi dimensioni

@PreFilter è davvero interessante e facile da usare, ma può essere inefficiente quando si tratta di elenchi molto grandi poiché l'operazione di recupero recupererà tutti i dati e applicherà il filtro in seguito.

Immagina, ad esempio, di avere migliaia di attività nel nostro database e di voler recuperare le cinque attività attualmente assegnate a pam . Se usiamo @PreFilter , l'operazione di database recupererà prima tutte le attività e le itererà tutte per filtrare quelle non assegnate a pam .

5. conclusione

Questo rapido articolo spiega come creare un'applicazione semplice ma sicura utilizzando le annotazioni @PreFilter e @PostFilter di Spring Security .

Controlla l'esempio di codice completo in questo repository Github.