Integrar el login de Google en tu App con OAuth2 y Spring Security

4
39343

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:

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.

El esquema general del proceso de login con OAuth

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

Consola en Google APIs para dar de alta nuestro 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:

Creación de un cliente para el acceso al API de OAuth de Google

Pulsamos en el botón «Create an OAUTH 2.0 client ID…» e introducimos los datos de nuestra aplicación

Detalles del cliente para el acceso al API de OAuth de Google

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

Tipo del cliente para el acceso al API de OAuth de Google

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».

Datos a usar en el cliente para el acceso al API de OAuth de Google

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

Modificaión de la URL de callback en la consola de Google

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

Página de login para nuestra aplicación

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:

Login con 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)

Pagina privada en nuestra aplicación

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

David Gómez García
Titulado en Ingeniería Técnica en Informática por la Universidad Politécnica de Madrid en 1998, David ha participado en proyectos para telecomunicaciones, comercio electrónico, banca, defensa y sistemas de transporte terrestre. Ha liderado proyectos de aplicaciones web, infraestructura SOA, aplicaciones java cliente y pasarelas de comunicación entre sistemas.

4 COMENTARIOS

  1. 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.

  2. 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

  3. 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

  4. 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?

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad