Authentification unique simple avec Spring Security OAuth2

1. Vue d'ensemble

Dans ce didacticiel, nous verrons comment implémenter SSO - Single Sign On - en utilisant Spring Security OAuth et Spring Boot, en utilisant Keycloak comme serveur d'autorisation.

Nous utiliserons 4 applications distinctes:

  • Un serveur d'autorisation - qui est le mécanisme d'authentification central
  • Un serveur de ressources - le fournisseur de Foo s
  • Deux applications clientes - les applications utilisant SSO

En termes simples, lorsqu'un utilisateur tente d'accéder à une ressource via une application client, il sera redirigé pour s'authentifier en premier, via le serveur d'autorisation. Keycloak connectera l'utilisateur et, tout en étant connecté à la première application, si la deuxième application client est accédée à l'aide du même navigateur, l'utilisateur n'aura pas besoin de saisir à nouveau ses informations d'identification.

Nous allons utiliser le type d'octroi de code d'autorisation en dehors d'OAuth2 pour piloter la délégation d'authentification.

Nous utiliserons la pile OAuth dans Spring Security 5. Si vous souhaitez utiliser la pile héritée Spring Security OAuth, jetez un œil à cet article précédent: Simple Single Sign-On avec Spring Security OAuth2 (pile héritée)

Selon le guide de migration:

Spring Security fait référence à cette fonctionnalité en tant que connexion OAuth 2.0 tandis que Spring Security OAuth y fait référence en tant que SSO

D'accord, allons-y.

2. Le serveur d'autorisation

Auparavant, la pile Spring Security OAuth offrait la possibilité de configurer un serveur d'autorisation en tant qu'application Spring.

Cependant, la pile OAuth a été déconseillée par Spring et nous allons maintenant utiliser Keycloak comme serveur d'autorisation.

Donc, cette fois, nous allons configurer notre serveur d'autorisation en tant que serveur Keycloak intégré dans une application Spring Boot .

Dans notre pré-configuration, nous définirons deux clients, ssoClient-1 et ssoClient-2 , un pour chaque application client.

3. Le serveur de ressources

Ensuite, nous avons besoin d'un serveur de ressources ou de l'API REST qui nous fournira les Foo que notre application cliente consommera.

C'est essentiellement le même que nous avons utilisé pour nos applications client angulaire précédemment.

4. Les applications client

Regardons maintenant notre application cliente Thymeleaf; nous allons, bien sûr, utiliser Spring Boot pour minimiser la configuration.

Gardez à l'esprit que nous aurons besoin de deux d'entre eux pour démontrer la fonctionnalité d'authentification unique .

4.1. Dépendances de Maven

Tout d'abord, nous aurons besoin des dépendances suivantes dans notre 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 

Pour inclure tout le support client dont nous aurons besoin, y compris la sécurité, nous devons simplement ajouter spring-boot-starter-oauth2-client . De plus, étant donné que l'ancien RestTemplate va être obsolète, nous allons utiliser WebClient , et c'est pourquoi nous avons ajouté spring-webflux et reacteur-netty .

4.2. Configuration de la sécurité

Ensuite, la partie la plus importante, la configuration de sécurité de notre première application 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 partie principale de cette configuration est la méthode oauth2Login () , qui est utilisée pour activer la prise en charge de la connexion OAuth 2.0 de Spring Security. Étant donné que nous utilisons Keycloak, qui est par défaut une solution d'authentification unique pour les applications Web et les services Web RESTful, nous n'avons pas besoin d'ajouter de configuration supplémentaire pour SSO.

Enfin, nous avons également défini un bean WebClient pour agir comme un simple client HTTP pour gérer les demandes à envoyer à notre serveur de ressources.

Et voici l' 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/ 

Ici, spring.security.oauth2.client.registration est l'espace de noms racine pour l'enregistrement d'un client. Nous avons défini un client avec un identifiant d'enregistrement personnalisé . Ensuite, nous avons défini son identifiant client , son secret client , sa portée , son type d' autorisation et son uri de redirection , qui, bien sûr, devraient être les mêmes que ceux définis pour notre serveur d'autorisation.

Après cela, nous avons défini notre fournisseur de services ou le serveur d'autorisation, toujours avec le même identifiant personnalisé , et répertorié ses différents URI que Spring Security doit utiliser. C'est tout ce que nous devons définir, et le framework effectue tout le processus de connexion, y compris la redirection vers Keycloak, de manière transparente pour nous .

Notez également que, dans notre exemple ici, nous avons déployé notre serveur d'autorisation, mais bien sûr, nous pouvons également utiliser d'autres fournisseurs tiers tels que Facebook ou GitHub.

4.3. Le controlle

Implémentons maintenant notre contrôleur dans l'application client pour demander Foo à notre serveur de ressources:

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

Comme nous pouvons le voir, nous n'avons qu'une seule méthode ici qui fournira les ressources au modèle foos . Nous n'avons pas eu à ajouter de code pour la connexion.

4.4. L'extrémité avant

Voyons maintenant la configuration frontale de notre application cliente. Nous n'allons pas nous concentrer sur cela ici, principalement parce que nous avons déjà couvert le site.

Notre application client a ici un front-end très simple; voici l' index.html :

Spring OAuth Client Thymeleaf - 1 Welcome !

Login

Et le foos.html :

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

La page foos.html nécessite que les utilisateurs soient authentifiés. Si un utilisateur non authentifié tente d'accéder à foos.html , il sera d'abord redirigé vers la page de connexion de Keycloak .

4.5. La deuxième application 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

Dans ce didacticiel, nous nous sommes concentrés sur la mise en œuvre de l'authentification unique à l'aide de Spring Security OAuth2 et de Spring Boot en utilisant Keycloak comme fournisseur d'identité.

Comme toujours, le code source complet peut être trouvé sur GitHub.