Spring Security – Securización básica

Como ya adelantamos en la entrada anterior, el proceso de aprendizaje y demostración de Spring Security lo llevaremos a cabo construyendo una aplicación web. Para ser más precisos, se trata de una aplicación web colaborativa que permitirá a los usuarios organizar, crear y compartir sus colecciones musicales (álbumes, discos, artistas). Además, podrán planificar y tener noticias actualizadas de los últimos eventos musicales, crear anuncios de trabajo relacionados con la música, listas de reproducción, etc. Actualmente, solo una pequeña parte de la funcionalidad anteriormente dicha está implementada, pero que iremos completando en las próximas entradas. En la siguiente imagen se aprecia la página principal de nuestra aplicación.

Se pueden diferenciar claramente tres zonas:

  • La cabecera, donde se encuentra el logo, el componente para las búsquedas y algunos botones de acceso directo.
  • El contenido principal.
  • El pie de página donde está ubicado el tablón de anuncios y paneles para acceder a páginas de competición, recomendaciones musicales, etc.

Hemos decidido usar Spring MVC por su facilidad de mapeo de peticiones y por el hecho de que tendremos a nuestra disposición todas esas marcianadas exóticas que ya comentamos: inyección de dependencia, AOP, abstracciones para el acceso a datos, gestión transparente de transacciones, etc. Sin embargo, si nuestro punto fuerte no es precisamente Spring MVC, podemos usar otro framework web, siempre y cuando se pueda integrar con Spring (la mayoría ofrecen integración). Para persistir los datos de nuestro sistema, vamos a usar el servidor de bases de datos MySQL, aunque nada nos impide usar cualquier otro sistema gestor compatible con el estándar JDBC. En un futuro, sería interesante usar algún sistema de almacenamiento de datos de la nueva generación, como MongoDB, orientado a trabajo con colecciones y modelos libres de esquema, Neo4J, base de datos basada en grafos o incluso almacenamiento en la nube.

Por último, mencionar que el motor de plantillas usado es Sitemesh por la facilidad que ofrece a la hora de definir los decoradores de contenido. También vamos a usar JQuery y un montón de plugins de este versátil framework JavaScript.

Después de presentar brevemente las tecnologías usadas, he aquí el resumen de lo que aprenderemos esta vez :

  • Configurar Spring Security.
  • Habilitar la autenticación básica.
  • Definir el gestor y proveedor de autenticación basado en memoria.
  • Declarar filtros interceptores para proteger las URL.
  • Proteger los métodos de nuestro proceso de negocio.

En primer lugar, descargamos la distribución de Spring Security desde la página del proyecto. En el momento de estar escribiendo esta entrada, la versión de producción es la 3.0.7. Una vez descomprimida, añadimos al directorio lib de nuestro proyecto las librerías que aparecen seleccionadas en la imagen.

Obviamente, necesitaremos el resto de librerías de Spring para arrancar la aplicación. A continuación hemos de declarar varios descriptores web, exactamente la clase listener (ContextLoaderListener) que cargará el contenedor y inicializará los beans, y el filtro DelegatingFilterProxy cuyo propósito es envolver y securizar todas las peticiones aplicando la cadena de filtros. Este también actúa como pasarela entre el contexto de aplicación y los servlets / filtros. Añadimos las siguientes líneas al fichero 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>
	
<context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>/WEB-INF/securityApplicationContext.xml</param-value>
</context-param>
	
<listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

En el fichero securityApplicationContext.xml vamos a tener las declaraciones de los beans específicos de Spring Security. A partir de la versión 2.0.1, Spring Security ofrece el espacio de nombre security que reduce considerablemente la cantidad de beans a declarar para habilitar la funcionalidad básica. Usando los elementos del namespace anteriormente dicho, podemos habilitar la autenticación básica de la siguiente forma:

<security:http auto-config="true">
    	
</security:http>
    
<security:authentication-manager alias="authenticationManager">
    <security:authentication-provider>
    	<security:user-service>
    		<security:user name="nedo" password="nedo"   authorities="ROLE_USER"/>
    		<security:user name="admin" password="admin" authorities="ROLE_ADMIN"/>
   		</security:user-service>
   	</security:authentication-provider>
</security:authentication-manager>

Si ahora “redesplegamos” la aplicación y intentamos acceder, se nos presentará la página similar a la mostrada en la imagen:

¡Sin escribir ni una línea de código, hemos habilitado la autenticación en nuestra aplicación! Podemos intentar autenticarnos con el nombre de usuario inexistente o contraseña incorrecta, para comprobar que efectivamente el proceso de autenticación no se realizará con éxito. Por detrás, Spring Security se ha encargado de declarar una serie de artefactos:

  • AuthenticationManager, el gestor de autenticación cuya responsabilidad es validar las credenciales del usuario y enriquecer el objeto Authentication con información adicional. Este objeto normalmente contendrá el nombre de usuario, o los roles que se hayan asignado a la entidad.
  • AuthenticationProvider, que provee al gestor de autenticación de datos que éste usará para decidir si las credenciales proporcionadas por el usuario son correctas. En nuestro caso los datos de usuario se almacenarán en memoria y en texto plano, usando el elemento <security:user-service>. Cada usuario del sistema se definirá a través del elemento </security><security :user>, cuyo atributo name será el nombre de usuario, password la contraseña y el atributo authorities contendrá uno o varios roles asignados al usuario. Evidentemente para una aplicación en producción esto no nos va a servir, pero para propósitos de demostración o pruebas, de momento nos vale.

  • AbstractAuthenticationProcessingFilter, que se encargará de procesar y extraer las credenciales de usuario de la petición HTTP e inicializar el objeto Authentication.

Nuestro siguiente objetivo es securizar las URL para que solamente los usuarios con permisos adecuados puedan acceder a éstas. Para este propósito tenemos a nuestra disposición el elemento <security:intercept-url>. Queremos que únicamente los usuarios con el rol ADMIN puedan acceder a la página para la creación de géneros musicales:

<security:http auto-config="true">
	<security:intercept-url pattern="/genres/create" access="ROLE_ADMIN"/>
	<security:intercept-url pattern="/*" access="ROLE_USER, ROLE_ADMIN"/>
</security:http>

Aquí hemos declarado dos reglas de intercepción dentro del contexto del elemento <security:http>. Como se puede apreciar, el atributo pattern define el patrón de la URL a proteger, y access serán uno o varios roles a los que se otorgará el acceso. Es importante el orden en el que se definen las reglas, por lo tanto las más específicas las pondremos al principio. Si ahora nos autenticamos con el usuario nedo (que tiene el rol USER),y intentamos crear un nuevo género, se nos denegará el acceso mostrando la siguiente página:

En las sentencias de intercepción podemos usar el lenguaje de expresiones de Spring (SpEL), para construir reglas más complejas mediante el uso de métodos, propiedades, etc. Para habilitar dichas expresiones, tenemos que inicializar el atributo use-expressions del elemento <security:http>:

<security:http auto-config="true" use-expressions="true">
    <security:intercept-url pattern="/genres/create" access="hasRole('ROLE_ADMIN')"/>
    <security:intercept-url pattern="/*" access="hasAnyRole('ROLE_ADMIN','ROLE_USER')"/>
</security:http>

Aquí podemos observar el uso de los métodos hasRole y hasAnyRole gracias al lenguaje de expresiones. Para la lista completa de métodos y pseudo propiedades disponibles, podemos consultar la referencia de Spring Security.

El filtro FilterSecurityInterceptor delega la responsabilidad al gestor de decisión AccessDecisionManager para decidir que petición se aceptará. Teniendo en cuenta el contexto de la petición y las autoridades del usuario, otro componente llamado Voter (votante), puede emitir el voto de acceso concedido, acceso denegado o abstenerse si no dispone de información suficiente. En el caso del elemento <security:intercept-url> el votante recibe la información del contexto de la petición a través del atributo access. La implementación por defecto del gestor de decisión en Spring Security es AffirmativeBased, por lo tanto con que uno de los votantes emita el voto de acceso concedido el acceso al recurso protegido se permitirá. En las próximas entradas veremos como alterar este funcionamiento, proporcionando la declaración del gestor de decisión y votantes.

En este punto, nuestra aplicación ha pasado de estar totalmente desprotegida a disponer del mecanismo de autenticación y concesión de accesos a los recursos en función de los privilegios del usuario. Pero, aun así, si el usuario malicioso consigue penetrar la capa web de nuestra aplicación puede comprometer la seguridad global de la misma. Necesitamos añadir otra capa de protección, y securizar la invocación de métodos de nuestro proceso de negocio. Como ya dijimos, Spring Security permite proteger la invocación de métodos, bien mediante anotaciones o declaración de puntos de corte. En esta ocasión vamos a ver como proteger los métodos usando la anotación PreAuthorize. Los usuarios con el rol ADMIN podrán listar, borrar o modificar los géneros musicales, pero el resto de usuarios tan solo podrá visualizar los géneros. En la imagen se muestra el aspecto de la página:

El primer paso consiste en habilitar las anotaciones de autorización:

<security:global-method-security pre-post-annotations="true" />

Es importante que la declaración del elemento anterior se haga en el contexto de aplicación raíz, es decir donde tengamos definida la referencia del bean AlbumGenreService. La anotación la vamos a colocar sobre el método en la interfaz del servicio:

public interface AlbumGenreService {
	@PreAuthorize("hasRole('ROLE_ADMIN')")
	public void deleteGenre(Integer genreId);
}

De esta forma el método solamente se podrá invocar si el usuario dispone del rol ADMIN. Podemos hacer la prueba, entrando otra vez con el usuario nedo, e intentar eliminar un género. El resultado se puede ver en la imagen:

En esta entrada se han visto los conceptos básicos, pero importantes para seguir con soltura el resto de entradas. Se recomienda al lector la descarga de la aplicación web [artgasmator] para estudiar bien su arquitectura y todos los aspectos anteriormente vistos.

La próxima vez veremos cómo integrar la página de login y logout con el look & feel de nuestra aplicación, implementar el renderizado condicional, la gestión de usuarios e almacenamiento y protección de las credenciales en las bases de datos relacionales.

Hasta entonces, ¡happy spring-securing!

Comments

  1. Buen 2undo post!
    Si no he entendido mal, @PreAuthorize(“hasRole(‘ROLE_ADMIN’)”) vendría a ser lo mismo que en ASP.NET sería el filtro de [Authorize(Role=”ROLE_ADMIN”)]?

    Saludos!

  2. Si no me equivoco, en ASP.NET solo se pueden anotar los métodos de los controladores. En SS, con PreAuthorize puedes anotar cualquier método, además es mucho más potente (permite el uso del lenguaje de expresiones, etc). Es tan solo una de las alternativas que ofrece Spring Security para proteger la capa de neogocio.

    Saludos

  3. Efectivamente, los filtros se aplican únicamente en los métodos del controlador.
    Gracias por la aclaración.

    Un saludo!

  4. Excelente….. Muy detallado y entendible, me podrian informar cuando sale el próximo tutorial

    Saludos

  5. http://Dudoso says

    Pero como se corre? En que ide lo puedo abrir? ya agregue todas las librerias tanto en eclipse como en netbeans y solo consigo errores 500 y 404, ninguna pagina se me ha mostrado hasta ahora, ni siquiera la de login, la base de datos ya existe, pero no he podido ver el home.jsp…agradezco su colaboracion.

Trackbacks

  1. […] con un post cargado de temas interesantes sobre nuestro framework de seguridad favorito. En la última entrada aprendimos los conceptos básicos de Spring Security – protección de las URLs, autenticación básica, securización de métodos de la capa de […]