Introduzione
Negli articoli precedenti della serie abbiamo parlato prevalentemente di autenticazione degli utenti, fornendo solamente un rapido accenno alla questione delle autorizzazioni. L’estrema potenza e semplicità del framework Spring Security è particolarmente evidente nella gestione delle ACL (Accesso Control List) che possono essere espresse semplicemente mediante Spring Expression Language (SpEL). Ma iniziamo per gradi parlando innanzitutto del concetto di ruolo e privilegio.
Ruoli e Privilegi
Da un punto di vista teorico in un sistema Rule Based ACL il concetto di ruolo è utilizzato per indicare una funzioni che un utente può assumere sul sistema (es. admin, editor, etc.) mentre i privilegi sono specifiche autorizzazioni associate ad un ruolo (es. lettura o scrittura di un campo).
In Spring Security questa distinzione non è molto netta e spesso ruoli (o Rule) e privilegi (o Authority) sono intercambiabili al punto che l’interfaccia UserDetails,
introdotta nell’articolo Primi Passi con Spring Security (parte 2), presenta un solo metodo getAuthorities(
) che restituisce sia i ruoli che i privilegi come stringhe.
La distinzione tra ruoli e privilegi avviene semplicemente utilizzando il prefisso ROLE
. Ad esempio il seguente XML definisce tre utenti: il primo user ha il ruolo USER
, il secondo reader ha il permesso CAN_READ
, mentre il terzo admin ha sia il ruolo USER
che i permesso CAN_READ
.
1 2 3 4 5 |
<user-service> <user name="user" password="{noop}password" authorities="ROLE_USER" /> <user name="reader" password="{noop}password" authorities="CAN_READ" /> <user name="admin" password="{noop}password" authorities="ROLE_USER,CAN_READ" /> </user-service> |
Privilegi e ruoli possono essere utilizzati per sia per controllare l’accesso ad una intera pagina web (full page authorization), che per visualizzare o meno porzioni di pagina (in page authorization).
Full Page Authorization
L’accesso ad una specifica pagina è controllabile filtrandone opportunamente l’URL attraverso il tag <intercept-url>
all’interno del tag <html>
dove abbiamo abilitato l’uso delle espressioni. Fondamentalmente il tag prevede due attributi:
- pattern: definisce l’URL filtrato anche utilizzando espressioni con *;
- access: definisce ruoli o privilegi che possono accedere utilizzando opportune espressioni.
La seguente porzione di codice XML definisce alcuni semplici interceptor. Le espressioni utilizzate per il controllo degli accessi sono sufficientemente intuitive ma vengono comunque descritte nel seguito dell’articolo.
1 2 3 4 5 6 7 |
<http auto-config="true" use-expressions="true"> <intercept-url pattern="/index.jsp" access="permitAll"/> <intercept-url pattern="/views/user.jsp" access="hasRole('USER')"/> <intercept-url pattern="/views/canRead.jsp" access="hasAuthority('CAN_READ')"/> <intercept-url pattern="/views/denyAll.jsp" access="denyAll"/> <intercept-url pattern="/views/*" access="isAuthenticated()"/> </http> |
Si noti che Spring valuta i filtri nell’ordine in cui compaiono, caratteristica che può essere utilizzata per definire accessi ad aree dell’applicazione in modo puntuale. Nell’esempio, infatti, tutte le pagine della cartella views
sono accessibili agli utenti autenticati (ultima regola nell’elenco), ad eccezione delle pagine user.jsp
, canRead.jsp
e denyAll.jsp
che hanno filtri specifici. Se l’ordine delle regole fosse stato invertito, tutte le pagine sotto views
sarebbero state visibili a tutti gli utenti indipendentemente da ruoli e privilegi, perchè la prima regola sarebbe stata applicata senza valutare le successive.
In Page Authorization
In questo caso è possibile “profilare” il contenuto di una pagina autorizzando solamente utenti con determinati ruoli o privilegi alla visualizzazione delle informazioni associate. Per farlo è innanzitutto necessario introdurre la dipendenza alla tag library di Spring Security:
1 2 3 4 5 |
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>5.0.3.RELEASE</version> </dependency> |
Successivamente dobbiamo abilitare il security namespace sulla pagina ed utilizzarlo per profilare le porzioni di codice di interesse:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags"%> <html> <body> <h3>This page has information not visible to all</h3> <security:authorize access="hasRole('ROLE_USER')"> <h3>This text is only visible to a user having role USER</h3> </security:authorize> <security:authorize access="hasAuthority('CAN_READ')"> <h3>This text is only visible to a user having profile CAN_READ</h3> </security:authorize> </body> </html> |
Espressioni Comuni
Le espressioni utilizzate per il controllo degli accessi sono valutate su un root object che è parte del contesto di Spring. L’oggetto root di base è SecurityExpressionRoot
e definisce termini e predicati di sicurezza comuni, disponibili sia in un contesto web che per la sicurezza dei metodi (non analizzati in questo articolo). La seguente tabella elenca le espressioni più comuni.
hasRole([role]) hasAnyRole([role1,role2]) |
Restituisce TRUE se l’utente corrente ha il ruolo o uno dei ruoli elencati. | ||
hasAuthority([authority]) hasAnyAuthority([authority1,authority2]) |
Restituisce TRUE se l’utente corrente ha il profilo o uno dei profili elencati. | ||
permitAll |
Restituisce sempre TRUE. | ||
denyAll |
Restituisce sempre FALSE. | ||
isAnonymous() |
Restituisce TRUE se l’utente corrente è anonimo. | ||
isRememberMe() |
Restituisce TRUE se l’utente corrente è stato riconosciuto in quanto aveva una sessione già aperta. | ||
isAuthenticated() |
Restituisce TRUE se l’utente non è anonimo. | ||
isFullyAuthenticated() |
restituisce TRUE se l’utente non è anonimo e non proviene da una sessione precedente (remember me). Utilizzato per forzare nuovamente l’accesso. | ||
principal authentication |
Termini che consentono l’accesso alle informazioni associate all’utente corrente ad esempio per essere visualizzate su una pagina jsp:
|
||
hasIpAddress(ipAddr) |
In ambito web limita l’accesso ai soli client con l’ip specificato. |
Personalizzazione
Se si desidera è possibile personalizzare o estendere le espressioni utilizzate per il controllo degli accessi semplicemente definendo un bean (gestito da Spring) ed utilizzandolo come una normale espressione. Ad esempio il seguente metodo check
del bean WebSecurity
:
1 2 3 4 5 6 |
public class WebSecurity { /* Equivalente a pemrmitAll */ public boolean check(Authentication authentication, HttpServletRequest request) { return true; } } |
<intercept-url>
nel seguente modo:
1 |
<intercept-url pattern="/index.jsp" access="@webSecurity.check(authentication,request)"/> |
E’ possibile anche utilizzare metodi che referenziano path variable definiti nell’URL dell’attributo pattern, ad esempio:
1 2 |
<intercept-url pattern="/user/{userId}/**" access="@webSecurity.checkUserId(authentication,#userId)"/> |
Codice Sorgente
Il codice sorgente completo degli esempi presentati è scaricabile qui spring-security.