Integrar el login de Google en tu App con OAuth2 y Spring Security
En este tutorial, vamos a ver cómo montar en tres sencillos pasos la seguridad de nuestra aplicación con Spring Security, pero delegando la autenticación en un proveedor externo de OAuth2 como Google (o también podría ser Facebook, twitter, Linkedin u otros).
¿Qué significa esto?, básicamente que delegaremos el control de usuario y password (la autenticación) en un sistema ajeno, de forma que no tenemos que preocuparnos de la gestión de usuarios (darlo de alta, gestionar sus contraseñas, sus recordatorios, etc..) ya que ese mantenimiento ya lo lleva a cabo el proveedor de OAuth contra el que conectamos.
En nuestra aplicación, por tanto, sólo nos preocuparemos de delimitar las restricciones de seguridad (la autorización); esto es, a quién o a qué tipo de usuarios dejamos acceder a qué partes.
Empezaremos por crear una aplicación web que tendrá una parte pública (accesible a cualquier usuario) y una parte privada (sólo para usuarios registrados). Configuraremos Spring Security para especificar las restricciones de seguridad de la parte privada. Por último configuraremos el sistema de autenticación de Spring Security, utilizando una librería con implementación de proveedores específicos de SpringSecurity para OAuth, para integrar el proceso de identificación del usuario (autenticación).
Iremos viendo estos pasos en los siguientes apartados:
- Estructura básica de la aplicación
- Obtener las API Keys para poder utilizar el Login de Google.
- Configurando Spring Security en nuestra aplicación.
- Configuración de Google como proveedor OAuth de autenticación
- La prueba de la aplicación
Una vez que tengamos configurada la autenticación utilizando un proveedor de OAuth, evolucionaremos nuestro sistema, incluyendo soporte a varios proveedores.
Estructura básica de la aplicación.
Nuestra aplicación de ejemplo constará de una «Landing Page», que consideraremos pública y a la que podrá acceder cualquier usuario. En la landing page, daremos la opción de que el usuario acceda a la parte privada, donde se mostrará su nombre y sus credenciales asignadas dentro de la aplicación. Esta página con la descripción del perfil será la parte que consideraremos privada y, por tanto, sólo de acceso para usuarios registrados.
Vamos a utilizar Spring MVC para la aplicación web. Así que comenzamos por definir en el Web.xml el DispatcherServlet de Spring:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <servlet> <servlet-name>springWeb</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>springWeb</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
Una vez configurado el DispatcherServlet de Spring, configuramos los componentes de infraestructura de Spring MVC en el fichero {servlet-name}-servlet.xml. En este caso, el fichero se llamará springWeb-servlet.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> <mvc:annotation-driven/> <mvc:resources mapping="/js/**" location="/js/"/> <mvc:resources mapping="/img/*" location="/img/"/> <mvc:resources mapping="**.html" location="/"/> <mvc:resources mapping="/css/**" location="/css/"/> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
Con la estructura de Spring y de la aplicación web creadas, utilizaremos como página pública una versión ligeramente modificada de uno de los ejemplos de Twitter Bootstrap, en la que incluimos un botón con un enlace a la página privada:
<a class="btn btn-large btn-primary right" href="/userHome">Entra</a>
Ahora definiremos un controlador de Spring MVC que simplemente pondrá información del perfil del usuario disponible en la vista:
package com.dgomezg.playground.spring.oauth.controller; import org.springframework.security.access.annotation.Secured; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class HomeController { @RequestMapping(value="/userHome", method = RequestMethod.GET) public void getUserProfile(Model model) { model.addAttribute("user", SecurityContextHolder.getContext().getAuthentication().getPrincipal()); } }
Por último, para acabar la estructura básica de la aplicación, preparamos la página privada donde mostraremos información del usuario registrado (en este caso será solo el perfil del usuario, para mantener el ejemplo sencillo). Esta página la crearemos en /WEB-INF/jsp/userHome.jsp
<html> <body> <div>Usuario Registrado: <%=user%></div> </body> </html>
Con esta estructura, tendremos ya la aplicación base sobre la que añadir la seguridad con el proceso de Login delegado en proveedores externos. Es normal que, en este punto, la aplicación dé un error al pulsar en el botón de «Entrar» de la parte pública. Eso se debe a que no hemos configurado todavía los procesos de seguridad.
Vamos ahora a ello. Vamos a empezar con integrar la validación de usuario con Google.
Obtener las API Keys para poder utilizar el Login de Google.
Para invocar el servicio de Login de Google, necesitamos obtener un código de uso (un API Key) para nuestra aplicación. Para ello, accederemos a la consola de APIs de Google.
Si no lo hemos hecho antes, el primer paso que necesitamos es crear un proyecto
Pulsamos en el botón «Create project…» y en la siguiente pantalla podemos seleccionar los servicios que vamos a querer utilizar de Google. En este caso, como sólo vamos a utilizar el Login, no es necesario seleccionar ningún servicio adicional; pasamos a obtener el API Key. Para ello, seleccionamos en el menú de la izquierda, la opción API Access:
Pulsamos en el botón «Create an OAUTH 2.0 client ID…» e introducimos los datos de nuestra aplicación
En el siguiente apartado, especificamos el tipo de aplicación, en nuestro caso, una aplicación web, e indicamos, para nuestro tutorial, que la URL es http://localhost
Pulsamos en el botón «Create client ID» y nos llevará al panel de control donde podremos ver el API Key generado que tendremos que configurar más adelante en nuestra aplicación. En concreto, necesitaremos los valores del «Client ID» y el «Client Secret».
Desde esta consola de administración podremos crear nuevos clientes de autorización («Create another client ID…») o modificar, por ejemplo la URL a la que Google deberá devolver la llamada después de haber realizado la autenticación («Edit settings…»). Volveremos sobre esto un poco más tarde.
Configurando Spring Security en nuestra aplicación.
Spring Security es un módulo del framework de Spring que nos permite configurar de forma muy sencilla la gestión de seguridad en nuestras aplicaciones web.
Se basa en un Filtro, o más bien en una cadena de filtros (SpringSecurityFilterChain), que se encargar de coordinar las distintas tareas que participan en el control de la seguridad de una aplicación Web.
Para añadir seguridad con Spring Security en nuestra aplicación web, comenzaremos por configurar dicho filtro de seguridad en nuestro descriptor web.xml:
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
Con esta configuración, se crea un servletFilter (DelegatingFilterProxy) que buscará en el contexto de configuración de aplicación de Spring, un bean con id «springSecurityFilterChain» en quien delegará las comprobaciones de seguridad.
Esta configuración de spring la incorporaremos a un fichero de contexto específico, que nombraremos como spring-security.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:security="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd"> <security:http> <security:intercept-url pattern="/userHome" access="IS_AUTHENTICATED_FULLY" /> <security:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" /> <security:logout /> </security:http> </beans>
De momento hemos configurado, a través de la etiqueta security:http, que utilizaremos seguridad a nivel de peticiones http. Al configurar esta etiqueta, Spring crea y configura el bean springSecurityFilterChain y todos los filtros que forman parte de dicha cadena de filtros de seguridad.
También hemos añadido dos patrones de URL con las restriciones de seguridad, de forma que la ruta /userHome solo será accesible para usuarios que estén autenticados y el resto para usuarios anónimos (es decir, aquellos que no se hayan identificado todavía).
Por úiltimo, debemos registrar un Listener que permita cargar y configurar este fichero de seguridad, de forma que el DelegatingFilterProxy funcione como esperamos. Para ello, completamos la definición de nuestro descriptor web.xml
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/spring-security.xml</param-value> </context-param> <listener> <description>Spring context loader listener</description> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
Con esta configuración, hemos configurado un ContextLoaderListener que se encargará de crear un contexto de Spring con la configuración de spring-security.xml. Este contexto de Spring quedará asociado al contenedor de Servlets y, por tanto, accesible para el DispatcherServlet o el DelegatingFilterProxy definido en el mismo web.xml
Con este apartado, ya tenemos configurada la gestión básica de seguridad, pero aún no hemos especificado cuál es el mecanismo de Login que queremos utilizar.
Configuración de Google como proveedor OAuth de autenticación
Para añadir la llamada a Google para realizar la autenticación, utilizaremos la librería spring-security-pac4j en la que ya se definen algunos componentes que, implementados como Proveedores de autenticación sobre Spring-Security, gestionan el detalle de la llamada a cada varios Proveedores de autenticación a través de OAuth.
Para este tutorial, nosotros nos limitaremos a utilizar el GoogleProvider, pero existen varias implementaciones adicionales, que nos permitirán configurar la autenticación de usuarios con Facebook, Linkedin, twitter, y varios otros.
Para utilizar los componentes de esta librería, añadiremos las coordenadas maven, a nuestra gestión de dependencias
<dependency> <groupId>com.github.leleuj.springframework.security</groupId> <artifactId>spring-security-oauth-client</artifactId> <version>1.1.0</version> </dependency>
Una vez añadidas las dependencias, podemos complementar la configuración de spring-security.xml para hacer uso del proveedor de login
<bean id="google2Provider" class="org.scribe.up.provider.impl.Google2Provider"> <property name="key" value="{pon aqui el Client ID creado en la consola de Google API (en el punto 1)}"/> <property name="secret" value="{secret Key de la consola de Google API}"/> <property name="scope" value="EMAIL_AND_PROFILE"/> </bean> <bean id="providersDefinition" class="org.scribe.up.provider.ProvidersDefinition"> <property name="baseUrl" value="http://localhost:8080/j_spring_oauth_security_check" /> <property name="providers"> <list> <ref bean="google2Provider" /> </list> </property> </bean> <bean id="oAuthProvider" class="com.github.leleuj.ss.oauth.client.authentication.OAuthAuthenticationProvider"> <property name="providersDefinition" ref="providersDefinition" /> </bean> < <property name="provider" ref="google2Provider" /> </bean> <bean id="oAuthFilter" class="com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationFilter"> <property name="providersDefinition" ref="providersDefinition" /> <property name="authenticationManager" ref="authenticationManager" /> </bean>
Por último, configuramos, también en spring-security.xml, para que el proveedor de autenticación de Spring utilice este proveedor que conecta con Google, y configuramos cual es el entry-point por defecto para la seguridad http:
<security:authentication-manager alias="authenticationManager"> <security:authentication-provider ref="oAuthProvider"/> </security:authentication-manager> <security:http entry-point-ref="google2EntryPoint"> <security:custom-filter after="CAS_FILTER" ref="oAuthFilter" /> <security:intercept-url pattern="/userHome" access="IS_AUTHENTICATED_FULLY" /> <security:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" /> <security:logout /> </security:http>
Por último, será necesario que volvamos a la consola de APIs de Google y modifiquemos, para nuestra aplicación, la URL a la que debe volver Google una vez que la autenticación haya sido realizada con éxito. Para ello, sobre el CLient ID que creamos en el segundo paso de este tutorial, pulsamos sobre el enlace «Edit Settings…» y añadimos http://localhost:8080/j_spring_oauth_security_check como URL de callback autorizada para el login
La prueba de la aplicación
Con esta configuración, podemos probar a desplegar nuestra aplicación y comprobar que la autenticación de google funciona correctamente
Una vez desplegada la aplicación, sin estar identificados aún en el sistema, accedemos con el navegador a la dirección de la página pública
Al pulsar en el botón «Entra», debería llevarnos a la url de la página protegida, pero saltará el filtro de seguridad que detecta que no estamos autenticados y, por tanto, no cumplimos las restricciones de seguridad especificadas en spring-security.xml. La acción configurada es autenticarnos utilizando el GoogleProvider que, nos llevará a la página de autenticación de Google:
Observamos que la URL ha cambiado y estamos identificándonos en el servidor de Google. Esta es una de las ventajas de OAuth, nuestra aplicación nunca tiene que gestionar el proceso de validación, siendo el proveedor de autenticación quién se encarga de ello y nos dará un token de usuario en caso de que dicha validación sea correcta.
AL introducir nuestro usuario y pasword correctos en Google, se nos redirgirá de nuevo a la parte protegida de nuestra aplicación que, en este caso, simplemente muestra el token de seguiridad devuelto por Google (tal y como lo habíamos configurado en nuestro controlador)
A partir de aquí, sólo tendríamos que ir añadiendo funcionalidad a nuestra aplicación, añadiendo nuevos controladores a Spring y especificando sus restriciones de seguridad
Cuando hemos configurado la autenticación delegando el registro en proveedores externos, es habitual, permitir a los usuarios de nuestra aplicación que puedan utilizar distintas cuentas personales (facebook, twitter, Linkedin, Google u otras). Para ello, una vez que hemos llegado a este punto, podriamos modificar ligeramente el proceso de login para permitir conectar con distintos proveedores de OAuth. Pero dejaremos esta modificación para un segundo tutorial que publicaré próximamente.
Por último, he dejado disponible el código de este ejemplo en mi repositorio GitHub con ejemplos de Spring
Hola David, muy buena informacion.
te cuento que estoy desarrollando una aplicacion web que pretende integrar una autentificaion por Google usando oAuth.
El problema que me surgió es que no logro encontrar la propiedad (que según he investigado se llama oauth_callback) que me redireccione a una página elegida por mi.
Te agradeceria si tenes algun conocimiento del tema, sobre donde ubicar la propiedad y la url específica a donde quiero me redireccione. Desde ya muchas gracias por la informacion, Saludos.
Hola,
Lo que pasa que cuando cierro sesion y quiero volver a ingresar en al app
pide otra vez permisos, se supone que previamente ya habia concedidio los permisos,
¿cómo podría hacerle para ya no volver a solicitar los permisos?
Gracias
Hola! Soy muy nueva en esto y seguì paso a paso y no logro que funcione. Despuès clone tu proyecto y me da el mismo error en la resoluciòn de la variable. Agreguè la librería servlet-api-x.y.jar y tampoco lo soluciona. Te agradezco si pudieses ayudarme. El error al correr el project es 403 El sitio web rechazó mostrar esta página web pero me aparece con error el archivo del servlet.
«user cannot be resolved to a variable»
Usuario Registrado:
Desde ya muchìsimas gracias.
Un saludo
Buenas tardes muy bueno tu post, por favor sabes si hay una forma de que al autenticarme pueda obtener el correo del usuario que ingreso al sistema?