Introduzione a Spring Security Expressions

1. Introduzione

In questo tutorial ci concentreremo su Spring Security Expressions e, naturalmente, su esempi pratici con queste espressioni.

Prima di esaminare implementazioni più complesse (come ACL), è importante avere una solida conoscenza delle espressioni di sicurezza, poiché possono essere abbastanza flessibili e potenti se usate correttamente.

Questo articolo è un'estensione di Spring Security Expressions - hasRole Example.

2. Dipendenze di Maven

Per utilizzare Spring Security, è necessario includere la seguente sezione nel file pom.xml :

  org.springframework.security spring-security-web 5.2.3.RELEASE  

L'ultima versione può essere trovata qui.

E una breve nota: questa dipendenza copre solo Spring Security; non dimenticare di aggiungere s pring-core e spring-context per un'applicazione web completa.

3. Configurazione

Per prima cosa, diamo un'occhiata a una configurazione Java.

Estenderemo WebSecurityConfigurerAdapter , in modo da avere la possibilità di agganciarsi a uno qualsiasi dei punti di estensione offerti dalla classe base:

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

Ovviamente possiamo anche fare una configurazione XML:

4. Espressioni di sicurezza web

Ora, iniziamo a guardare le espressioni di sicurezza:

  • hasRole , hasAnyRole
  • hasAuthority , hasAnyAuthority
  • allowAll , denyAll
  • isAnonymous , isRememberMe , isAuthenticated , isFullyAuthenticated
  • principal , autenticazione
  • hasPermission

E ora esaminiamo ciascuno di questi in dettaglio.

4.1. hasRole, hasAnyRole

Queste espressioni sono responsabili della definizione del controllo di accesso o dell'autorizzazione a URL o metodi specifici nell'applicazione.

Diamo un'occhiata all'esempio:

@Override protected void configure(final HttpSecurity http) throws Exception { ... .antMatchers("/auth/admin/*").hasRole("ADMIN") .antMatchers("/auth/*").hasAnyRole("ADMIN","USER") ... } 

In questo esempio specifichiamo l'accesso a tutti i collegamenti che iniziano con / auth / limited agli utenti che hanno effettuato l'accesso con il ruolo USER o il ruolo ADMIN. Inoltre, per accedere ai collegamenti che iniziano con / auth / admin / dobbiamo avere il ruolo ADMIN nel sistema.

La stessa configurazione può essere ottenuta nel file XML, scrivendo:

4.2. hasAuthority, hasAnyAuthority

I ruoli e le autorità sono simili in primavera.

La differenza principale è che i ruoli hanno una semantica speciale: a partire da Spring Security 4, il prefisso " ROLE_ " viene aggiunto automaticamente (se non è già presente) da qualsiasi metodo correlato al ruolo.

Quindi hasAuthority ('ROLE_ADMIN') è simile a hasRole ('ADMIN') perché il prefisso ' ROLE_ ' viene aggiunto automaticamente.

Ma l'aspetto positivo dell'utilizzo delle autorità è che non è necessario utilizzare affatto il prefisso ROLE_ .

Ecco un rapido esempio in cui definiamo utenti con autorità specifiche:

@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("user1").password(encoder().encode("user1Pass")) .authorities("USER") .and().withUser("admin").password(encoder().encode("adminPass")) .authorities("ADMIN"); } 

Possiamo quindi ovviamente usare queste espressioni di autorità:

@Override protected void configure(final HttpSecurity http) throws Exception { ... .antMatchers("/auth/admin/*").hasAuthority("ADMIN") .antMatchers("/auth/*").hasAnyAuthority("ADMIN", "USER") ... }

Come possiamo vedere, non stiamo affatto menzionando i ruoli qui. Inoltre, a partire dalla primavera 5, abbiamo bisogno di un bean PasswordEncoder :

@Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }

Infine, possiamo ovviamente ottenere la stessa funzionalità anche utilizzando la configurazione XML:

E:

4.3. allowAll, denyAll

Anche queste due annotazioni sono abbastanza semplici. Potremmo consentire l'accesso ad alcuni URL nel nostro servizio oppure negare l'accesso.

Diamo un'occhiata all'esempio:

... .antMatchers("/*").permitAll() ...

Con questa configurazione, autorizzeremo tutti gli utenti (sia anonimi che loggati) ad accedere alla pagina che inizia con "/" (ad esempio, la nostra homepage).

Possiamo anche negare l'accesso al nostro intero spazio URL:

... .antMatchers("/*").denyAll() ...

E ancora, la stessa configurazione può essere eseguita anche con una configurazione XML:

4.4. isAnonymous, isRememberMe, isAuthenticated, isFullyAuthenticated

In this subsection we focus on expressions related to login status of the user. Let's start with user that didn't log in to our page. By specifying following in Java config, we enable all unauthorized users to access our main page:

... .antMatchers("/*").anonymous() ...

The same in XML config:

If we want to secure the website that everybody who uses it will be required to login, we need to use isAuthenticated() method:

... .antMatchers("/*").authenticated() ...

or XML version:

Moreover, we have two additional expressions, isRememberMe() and isFullyAuthenticated(). Through the use of cookies, Spring enables remember-me capabilities so there is no need to log into the system each time. You can read more about Remember Me here.

In order to give the access to users that were logged in only by remember me function, we may use this:

... .antMatchers("/*").rememberMe() ...

or XML version:

Finally, some parts of our services require the user to be authenticated again even if the user is already logged in. For example, user wants to change settings or payment information; it's of course good practice to ask for manual authentication in the more sensitive areas of the system.

In order to do that, we may specify isFullyAuthenticated(), which returns true if the user is not an anonymous or a remember-me user:

... .antMatchers("/*").fullyAuthenticated() ...

or the XML version:

4.5. principal, authentication

These expressions allow accessing the principal object representing the current authorized (or anonymous) user and the current Authentication object from the SecurityContext, respectively.

We can, for example, use principal to load a user's email, avatar, or any other data that is accessible in the logged in user.

And authentication provides information about the full Authentication object, along with its granted authorities.

Both are described in further detail in the following article: Retrieve User Information in Spring Security.

4.6. hasPermission APIs

This expression is documented and intended to bridge between the expression system and Spring Security’s ACL system, allowing us to specify authorization constraints on individual domain objects, based on abstract permissions.

Let's have a look at an example. We have a service that allows cooperative writing articles, with a main editor, deciding which article proposed by other authors should be published.

In order to allow usage of such a service, we may create following methods with access control methods:

@PreAuthorize("hasPermission(#articleId, 'isEditor')") public void acceptArticle(Article article) { … }

Only authorized user can call this method, and also user needs to have permission isEditor in the service.

We also need to remember to explicitly configure a PermissionEvaluator in our application context:

dove customInterfaceImplementation sarà la classe che implementa PermissionEvaluator.

Ovviamente possiamo farlo anche con la configurazione Java:

@Override protected MethodSecurityExpressionHandler expressionHandler() { DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); expressionHandler.setPermissionEvaluator(new CustomInterfaceImplementation()); return expressionHandler; }

5. conclusione

Questo tutorial è un'introduzione completa e una guida a Spring Security Expressions.

Tutti gli esempi discussi qui sono disponibili nel progetto GitHub.