Inicio de sesión único simple con Spring Security OAuth2

1. Información general

En este tutorial, discutiremos cómo implementar SSO - Single Sign On - usando Spring Security OAuth y Spring Boot, usando Keycloak como el servidor de autorización.

Usaremos 4 aplicaciones separadas:

  • Un servidor de autorización, que es el mecanismo de autenticación central
  • Un servidor de recursos: el proveedor de Foo s
  • Dos aplicaciones cliente: las aplicaciones que utilizan SSO

En pocas palabras, cuando un usuario intenta acceder a un recurso a través de una aplicación de Cliente, será redirigido para autenticarse primero, a través del Servidor de Autorización. Keycloak iniciará la sesión del usuario, y mientras aún está conectado a la primera aplicación, si se accede a la segunda aplicación Cliente usando el mismo navegador, el usuario no necesitará ingresar sus credenciales nuevamente.

Vamos a utilizar el tipo de concesión de código de autorización de OAuth2 para impulsar la delegación de autenticación.

Usaremos la pila OAuth en Spring Security 5. Si desea usar la pila heredada de Spring Security OAuth, eche un vistazo a este artículo anterior: Inicio de sesión único simple con Spring Security OAuth2 (pila heredada)

Según la guía de migración:

Spring Security se refiere a esta función como inicio de sesión OAuth 2.0, mientras que Spring Security OAuth se refiere a ella como SSO

Muy bien, saltemos de inmediato.

2. El servidor de autorización

Anteriormente, la pila Spring Security OAuth ofrecía la posibilidad de configurar un servidor de autorización como una aplicación Spring.

Sin embargo, Spring ha desaprobado la pila OAuth y ahora usaremos Keycloak como nuestro servidor de autorización.

Entonces, esta vez, configuraremos nuestro servidor de autorización como un servidor Keycloak integrado en una aplicación Spring Boot .

En nuestra preconfiguración, definiremos dos clientes, ssoClient-1 y ssoClient-2 , uno para cada aplicación cliente.

3. El servidor de recursos

A continuación, necesitamos un servidor de recursos o la API REST que nos proporcionará los Foo que consumirá nuestra aplicación cliente.

Es esencialmente el mismo que usamos anteriormente para nuestras aplicaciones cliente Angular.

4. Las aplicaciones del cliente

Ahora echemos un vistazo a nuestra aplicación cliente Thymeleaf; Por supuesto, usaremos Spring Boot para minimizar la configuración.

Tenga en cuenta que necesitaremos 2 de estos para demostrar la funcionalidad de inicio de sesión único .

4.1. Dependencias de Maven

Primero, necesitaremos las siguientes dependencias en nuestro 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 

Para incluir todo el soporte al cliente que necesitaremos, incluida la seguridad, solo necesitamos agregar spring-boot-starter-oauth2-client . Además, dado que la antigua RestTemplate va a quedar obsoleta, usaremos WebClient , y es por eso que agregamos spring-webflux y reactor-netty .

4.2. Configuración de seguridad

A continuación, la parte más importante, la configuración de seguridad de nuestra primera aplicación cliente:

@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 central de esta configuración es el método oauth2Login () , que se utiliza para habilitar el soporte de inicio de sesión OAuth 2.0 de Spring Security. Dado que estamos usando Keycloak, que es de forma predeterminada una solución de inicio de sesión único para aplicaciones web y servicios web RESTful, no necesitamos agregar ninguna configuración adicional para SSO.

Finalmente, también definimos un bean WebClient para que actúe como un cliente HTTP simple para manejar las solicitudes que se enviarán a nuestro servidor de recursos.

Y aquí está el 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/ 

Aquí, spring.security.oauth2.client.registration es el espacio de nombres raíz para registrar un cliente. Definimos un cliente con id de registro personalizado . A continuación, definimos su ID de cliente , cliente-secreta , alcance , de tipo de concesión de autorización y redirección-uri , que por supuesto, debe ser el mismo que el definido para nuestro servidor de autorización.

Después de eso, definimos nuestro proveedor de servicios o el servidor de autorización, nuevamente con la misma identificación de personalizado , y enumeramos sus diferentes URI para que Spring Security los use. Eso es todo lo que necesitamos definir, y el marco realiza todo el proceso de inicio de sesión, incluida la redirección a Keycloak, sin problemas para nosotros .

También tenga en cuenta que, en nuestro ejemplo aquí, implementamos nuestro servidor de autorización, pero, por supuesto, también podemos utilizar otros proveedores de terceros como Facebook o GitHub.

4.3. El controlador

Ahora implementemos nuestro controlador en la aplicación cliente para solicitar Foo s de nuestro servidor de recursos:

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

Como podemos ver, solo tenemos un método aquí que distribuirá los recursos a la plantilla de foos . No tuvimos que agregar ningún código para iniciar sesión.

4.4. Interfaz

Ahora, echemos un vistazo a la configuración de front-end de nuestra aplicación cliente. No nos vamos a centrar en eso aquí, principalmente porque ya lo cubrimos en el sitio.

Nuestra aplicación cliente aquí tiene una interfaz muy simple; aquí está el index.html :

Spring OAuth Client Thymeleaf - 1 Welcome !

Login

Y el foos.html :

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

La página foos.html necesita que los usuarios estén autenticados. Si un usuario no autenticado intenta acceder a foos.html , primero será redirigido a la página de inicio de sesión de Keycloak .

4.5. La segunda aplicación de cliente

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

En este tutorial, nos enfocamos en implementar Single Sign-On usando Spring Security OAuth2 y Spring Boot usando Keycloak como proveedor de identidad.

Como siempre, el código fuente completo se puede encontrar en GitHub.