Single Sign-On semplice con Spring Security OAuth2

1. Panoramica

In questo tutorial, discuteremo come implementare SSO - Single Sign On - utilizzando Spring Security OAuth e Spring Boot, utilizzando Keycloak come server di autorizzazione.

Useremo 4 applicazioni separate:

  • Un server di autorizzazione, che è il meccanismo di autenticazione centrale
  • A Resource Server - il provider di Foo s
  • Due applicazioni client: le applicazioni che utilizzano SSO

In parole semplici, quando un utente tenta di accedere a una risorsa tramite un'app client, verrà reindirizzato per l'autenticazione prima, tramite il server di autorizzazione. Keycloak eseguirà l'accesso dell'utente e, sebbene sia ancora connesso alla prima app, se si accede alla seconda app client utilizzando lo stesso browser, l'utente non dovrà immettere nuovamente le proprie credenziali.

Utilizzeremo il tipo di concessione del codice di autorizzazione fuori OAuth2 per guidare la delega dell'autenticazione.

Useremo lo stack OAuth in Spring Security 5. Se desideri utilizzare lo stack legacy OAuth di Spring Security, dai un'occhiata a questo articolo precedente: Single Sign-On semplice con Spring Security OAuth2 (stack legacy)

Come da guida alla migrazione:

Spring Security si riferisce a questa funzione come accesso OAuth 2.0 mentre Spring Security OAuth si riferisce ad essa come SSO

Va bene, entriamo subito.

2. Il server di autorizzazione

In precedenza, lo stack OAuth di Spring Security offriva la possibilità di configurare un server di autorizzazione come applicazione Spring.

Tuttavia, lo stack OAuth è stato deprecato da Spring e ora utilizzeremo Keycloak come server di autorizzazione.

Quindi questa volta configureremo il nostro server di autorizzazione come un server Keycloak incorporato in un'app Spring Boot .

Nella nostra pre-configurazione, definiremo due client, ssoClient-1 e ssoClient-2 , uno per ciascuna applicazione client.

3. Il server delle risorse

Successivamente, abbiamo bisogno di un server delle risorse o dell'API REST che ci fornirà i Foo che la nostra app client utilizzerà.

È essenzialmente lo stesso che abbiamo utilizzato in precedenza per le nostre app client angolari.

4. Le applicazioni client

Ora diamo un'occhiata alla nostra applicazione client Thymeleaf; ovviamente utilizzeremo Spring Boot per ridurre al minimo la configurazione.

Tieni presente che avremo bisogno di 2 di questi per dimostrare la funzionalità Single Sign-On .

4.1. Dipendenze di Maven

Innanzitutto, avremo bisogno delle seguenti dipendenze nel nostro pom.xml :

 org.springframework.boot spring-boot-starter-web   org.springframework.boot spring-boot-starter-oauth2-client   org.springframework.boot spring-boot-starter-thymeleaf   org.thymeleaf.extras thymeleaf-extras-springsecurity5   org.springframework spring-webflux   io.projectreactor.netty reactor-netty 

Per includere tutto il supporto client di cui avremo bisogno, inclusa la sicurezza, dobbiamo solo aggiungere spring-boot-starter-oauth2-client . Inoltre, poiché il vecchio RestTemplate sarà deprecato, utilizzeremo WebClient , ed è per questo che abbiamo aggiunto spring-webflux e reattore-netty .

4.2. Configurazione della protezione

Successivamente, la parte più importante, la configurazione della sicurezza della nostra prima applicazione client:

@EnableWebSecurity public class UiSecurityConfig extends WebSecurityConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.antMatcher("/**") .authorizeRequests() .antMatchers("/") .permitAll() .anyRequest() .authenticated() .and() .oauth2Login(); } @Bean WebClient webClient(ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository) { ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 = new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, authorizedClientRepository); oauth2.setDefaultOAuth2AuthorizedClient(true); return WebClient.builder().apply(oauth2.oauth2Configuration()).build(); } }

La parte centrale di questa configurazione è il metodo oauth2Login () , utilizzato per abilitare il supporto per l'accesso OAuth 2.0 di Spring Security. Poiché utilizziamo Keycloak, che per impostazione predefinita è una soluzione Single Sign-On per app Web e servizi Web RESTful, non è necessario aggiungere ulteriori configurazioni per SSO.

Infine, abbiamo anche definito un bean WebClient che funga da semplice client HTTP per gestire le richieste da inviare al nostro Resource Server.

Ed ecco application.yml :

spring: security: oauth2: client: registration: custom: client-id: ssoClient-1 client-secret: ssoClientSecret-1 scope: read,write authorization-grant-type: authorization_code redirect-uri: //localhost:8082/ui-one/login/oauth2/code/custom provider: custom: authorization-uri: //localhost:8083/auth/realms/baeldung/protocol/openid-connect/auth token-uri: //localhost:8083/auth/realms/baeldung/protocol/openid-connect/token user-info-uri: //localhost:8083/auth/realms/baeldung/protocol/openid-connect/userinfo user-name-attribute: preferred_username thymeleaf: cache: false server: port: 8082 servlet: context-path: /ui-one resourceserver: api: project: url: //localhost:8081/sso-resource-server/api/foos/ 

Qui, spring.security.oauth2.client.registration è lo spazio dei nomi radice per la registrazione di un client. Abbiamo definito un client con ID di registrazione personalizzato . Quindi abbiamo definito il suo client-id , client-secret , ambito , autorizzazione-grant-type e redirect-uri , che ovviamente dovrebbero essere gli stessi definiti per il nostro server di autorizzazione.

Successivamente, abbiamo definito il nostro fornitore di servizi o il server di autorizzazione, sempre con lo stesso ID personalizzato , e abbiamo elencato i suoi diversi URI per Spring Security da utilizzare. Questo è tutto ciò che dobbiamo definire e il framework esegue l'intero processo di accesso, incluso il reindirizzamento a Keycloak, senza problemi per noi .

Si noti inoltre che, nel nostro esempio qui, abbiamo implementato il nostro server di autorizzazione, ma ovviamente possiamo anche utilizzare altri provider di terze parti come Facebook o GitHub.

4.3. Il controller

Implementiamo ora il nostro controller nell'app client per richiedere Foo dal nostro Resource Server:

@Controller public class FooClientController { @Value("${resourceserver.api.url}") private String fooApiUrl; @Autowired private WebClient webClient; @GetMapping("/foos") public String getFoos(Model model) { List foos = this.webClient.get() .uri(fooApiUrl) .retrieve() .bodyToMono(new ParameterizedTypeReference
    
     () { }) .block(); model.addAttribute("foos", foos); return "foos"; } }
    

Come possiamo vedere, qui abbiamo solo un metodo che distribuirà le risorse al modello foos . Non abbiamo dovuto aggiungere alcun codice per il login.

4.4. Fine frontale

Ora, diamo un'occhiata alla configurazione front-end della nostra applicazione client. Non ci concentreremo su questo qui, principalmente perché ne abbiamo già parlato sul sito.

La nostra applicazione client qui ha un front-end molto semplice; ecco l' indice.html :

Spring OAuth Client Thymeleaf - 1 Welcome !

Login

E il foos.html :

Spring OAuth Client Thymeleaf -1 Hi, preferred_username   
    
ID Name
No foos
ID Name

La pagina foos.html richiede che gli utenti siano autenticati. Se un utente non autenticato tenta di accedere a foos.html , verrà prima reindirizzato alla pagina di accesso di Keycloak .

4.5. La seconda applicazione client

We'll configure a second application, Spring OAuth Client Thymeleaf -2 using another client_idssoClient-2.

It'll mostly be the same as the first application we just described.

The application.yml will differ to include a different client_id, client_secret and redirect_uri in its spring.security.oauth2.client.registration:

spring: security: oauth2: client: registration: custom: client-id: ssoClient-2 client-secret: ssoClientSecret-2 scope: read,write authorization-grant-type: authorization_code redirect-uri: //localhost:8084/ui-two/login/oauth2/code/custom

And, of course, we need to have a different server port for it as well, so that we can run them in parallel:

server: port: 8084 servlet: context-path: /ui-two

Finally, we'll tweak the front end HTMLs to have a title as Spring OAuth Client Thymeleaf – 2 instead of – 1 so that we can distinguish between the two.

5. Testing SSO Behavior

To test SSO behavior, let's run our Applications.

We'll need all our 4 Boot Apps – the Authorization Server, the Resource Server and both Client Applications – to be up and running for this.

Now let's open up a browser, say Chrome, and log in to Client-1 using the credentials [email protected]/123. Next, in another window or tab, hit the URL for Client-2. On clicking the login button, we'll be redirected to the Foos page straightaway, bypassing the authentication step.

Similarly, if the user logs in to Client-2 first, they need not enter their username/password for Client-1.

6. Conclusion

In questo tutorial, ci siamo concentrati sull'implementazione del Single Sign-On utilizzando Spring Security OAuth2 e Spring Boot utilizzando Keycloak come provider di identità.

Come sempre, il codice sorgente completo può essere trovato su GitHub.