Ejemplo básico de autenticación contra BBDD usando Spring Security (parte I)

Spring Security antes conocido como Acegi es una API open source que provee de servicios de autenticación y autorización a las aplicaciones basadas en Spring. Desde su primera release se ha convertido en una de las mejores opciones y más extendidas para este fin.

Spring Security es una API muy potente y con muchas posibilidades. Permite la autenticación contra LDAP, Certificados digitales, etc. El ejemplo que viene a continuación muestra los pasos a seguir para la correcta autenticación de un usuario de una aplicación Web creada con Struts 1 (aunque es lo de menos) contra una base de datos.

  1. Paso 0 – Preliminares: Como punto de partida para el desarrollo del ejemplo haré uso del ejemplo struts-blank que viene en la página oficial de Struts 1. Lo descargo y lo descomprimo. El ejemplo simplemente contiene un index.jsp que redirige a una acción de Struts llamada Welcome.do. Lo voy a modificar para convertir el Welcome.do a un formulario que solicite el usuario y la password, para que, a través de Acegi sean validados contra la BBDD y en función de si las credenciales son o no válidas redirijir al usuario a una página que ponga “Estás dentro” o por el contrario a una página de error.
  2. Paso 1 – Creación del formulario de login: Editamos la jsp Welcome.jsp añadiendole un formulario como este:
    <form action="../j_acegi_security_check.do" method="post">
    	<input type="text" name="j_username">
    	<input type="password" name="j_password">
            <input type="submit">
    </form>
    

    Es importante tener en cuenta los nombres de los parámetro y el action. Los nombres aquí puestos son los que espera recibir Acegi por defecto así que si se cambian no se realizará la validación.

  3. Paso 2 – Creación de la jsp que se verá una vez autenticados: La llamo por ejemplo “EstoyDentro.jsp” y añado un texto. A continuación creo una nueva definición en los action mappings del struts-config.xml llamada EstoyDentro que redirige a la nueva jsp.
    <action
                path="/EstoyDentro"
                forward="/pages/EstoyDentro.jsp"/>
    
  4. Paso 3 – Añadir las librerias correspondientes al proyecto: Se obtienen de http://www.acegisecurity.org/downloads.html y se añaden en la carpeta lib.
  5. Paso 4 – Creación del fichero applicationContext-acegi-security.xml: Creo un fichero llamado applicationContext-acegi-security.xml en el directorio /WEB-INF. Este fichero contiene toda la información de contexto necesaria para que Acegi funcione y tiene esta pinta:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <!-- https://hermosodia.wordpress.com - Ejemplo básico de autenticación contra BBDD usando Spring Security -->
    
    <beans>    
       <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
          <property name="filterInvocationDefinitionSource">
             <value>
    	    	CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
    	    	PATTERN_TYPE_APACHE_ANT
    	    	/**=httpSessionContextIntegrationFilter, authenticationProcessingFilter, anonymousProcessingFilter, exceptionTranslationFilter, filterSecurityInterceptor	    	
             </value>
          </property>
        </bean>
    
      <bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">
          <property name="context">
    	      <value>org.acegisecurity.context.SecurityContextImpl</value>
          </property>
      </bean>
    
       <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
          <property name="authenticationManager"><ref bean="authenticationManager"/></property>
          <property name="authenticationFailureUrl"><value>/Welcome.do?error=true</value></property>
          <property name="defaultTargetUrl"><value>/EstoyDentro.do</value></property>
          <property name="alwaysUseDefaultTargetUrl"><value>true</value></property>
          <property name="filterProcessesUrl"><value>/j_acegi_security_check.do</value></property>
       </bean>
    
       <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
          <property name="providers">
           	<list>
                <ref bean="daoAuthenticationProvider"/>
    			<ref local="anonymousAuthenticationProvider"/>
            </list>
          </property>
         </bean>
       	  	<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
        	<property name="userDetailsService">
        		<ref bean="memoryAuthenticationDao"/>
        	</property>
      	</bean>
      	<bean id="memoryAuthenticationDao" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
      <property name="userMap">
        <value>
          pepe=pepe,ROLE_ADMINISTRADOR
        </value>
      </property>
    </bean>
      
    
    	
       <bean id="anonymousAuthenticationProvider" class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
          <property name="key"><value>foobar</value></property>
       </bean>
    
       <bean id="anonymousProcessingFilter" class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
          <property name="key"><value>foobar</value></property>
          <property name="userAttribute"><value>anonymousUser,ROLE_ANONYMOUS</value></property>
       </bean>
    
       <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
          <property name="authenticationEntryPoint"><ref local="authenticationProcessingFilterEntryPoint"/></property>
       </bean>
       <bean id="authenticationProcessingFilterEntryPoint" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
    		  <property name="loginFormUrl"><value>/Welcome.do</value></property>
    		  <property name="forceHttps"><value>false</value></property>
       </bean>
       
       
       <bean id="filterSecurityInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
          <property name="authenticationManager"><ref bean="authenticationManager"/></property>
          <property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property>
          <property name="objectDefinitionSource">
             <value>
    			    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
    			    PATTERN_TYPE_APACHE_ANT
    			     /welcome.do*=ROLE_ANONYMOUS
    			     /estoydentro.do*=ROLE_ADMINISTRADOR			    
    			    /*.do*=ROLE_ADMINISTRADOR
    	     </value>
          </property>
       </bean>
    
       <bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
          <property name="allowIfAllAbstainDecisions"><value>false</value></property>
          <property name="decisionVoters">
             <list>
                <ref bean="roleVoter"/>
             </list>
          </property>
       </bean>
    
      <bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter">
      		
      </bean>
       <bean id="loggerListener" class="org.acegisecurity.event.authentication.LoggerListener"/>
       
    </beans>
    

    Con este XML básico permitimos el acceso a /EstoyDentro.do a un usuario con el rol ROLE_ADMINISTRADOR. De momento el sistema se autentica contra un usuario definido en bean “memoryAuthenticationDao” dentro del propio XML este modo es muy últil para hacer pruebas en tiempo de desarrollo (posteriormente lo haremos contra BBDD).

    El bean “filterSecurityInterceptor” es el encargado de filtrar las entradas a ciertas urls en función del rol dado. Así pues, en el ejemplo se permite el acceso a /Welcome.do a cualquier usuario anónimo, por el contrario, sólo puede acceder a /EstoyDentro.do un usuario con el rol ROLE_ADMINISTRADOR. Debido al uso de CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON, los paths por los que filtrar han de estar en minúscula.

    Además de estos beans es importante tener en cuenta las propiedades de este otro “authenticationProcessingFilter”.

    • authenticationFailureUrl: Es la url a donde hay que redirigir la petición en caso de autenticación fallida. En el ejemplo nos redirige a la misma página que contiene el formulario de login pero con un parámetro para indicar que la autenticación ha fallado y mostrar el mensaje correspondiente al usuario /Welcome.do?error=true
    • defaultTargetUrl: Es la url a la que se redigirá la petición en caso de que el usuario se haya autenticado correctamente.
  6. Paso 5 – Referenciar al applicationContext-acegi-security.xml creado: Añadimos las siguientes líneas de código al fichero web.xml.
    <context-param>
    		<param-name>contextConfigLocation</param-name>
    
    		<param-value>
    			/WEB-INF/applicationContext-acegi-security.xml
    		</param-value>
    	</context-param>
    	
        <filter>
            <filter-name>Acegi Filter Chain Proxy</filter-name>
            <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
            <init-param>
    
                <param-name>targetClass</param-name>
                <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
            </init-param>
        </filter>
    
        <filter-mapping>
          <filter-name>Acegi Filter Chain Proxy</filter-name>
          <url-pattern>/*</url-pattern>
    
        </filter-mapping>
    	
    	<!--
    	  - Loads the root application context of this web app at startup.
    	  - The application context is then available via 
    	  - WebApplicationContextUtils.getWebApplicationContext(servletContext).
        -->
    	<listener>
    		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    	</listener>
    
  7. Paso 6 – Prueba de la aplicación: Despliego la aplicación en el servidor y la veo a través de navegador. El index.jsp me redirije a Welcome.do y este me muestra el formulario. Introduzco el usuario y la password indicada en el applicationContext-acegi-security.xml y tras aceptar… pues el sistema me da paso a EstoyDentro.do.

No más por hoy. Ya tengo un ejemplo de autenticación de una aplicación Struts contra Acegi con datos cargados en memoria. En la parte 2 que publicaré presumiblemente mañana ampliaré el ejemplo para logear al usuario contra una BBDD. También mostraré los pasos necesarios para la desencriptación de la password.

Me voy a echar un trujas y a planchar la oreja. Mañana será otro hermoso día😉

—–

Actualización (15 Agosto 2008): Ya está lista la segunda parte. Puedes obtenerla aquí.

11 Responses to “Ejemplo básico de autenticación contra BBDD usando Spring Security (parte I)”


  1. 1 Julio septiembre 25, 2008 a las 17:12

    Gran artículo. Me ha ayudado mucho. Reconozco que estaba un poco perdido integrando Spring Security con Struts..

    Sólo una cosa. Has puesto:

    Y quizas deberias aclarar que los ‘..’ deberian substituirse por el nombre de contexto de la aplicacion. Al menos yo con la distribucion de Spring Security (no acegi) he tenido que hacerlo para que funcione.

    Muchas gracias por todo.

  2. 2 jotadeveloper octubre 20, 2008 a las 17:55

    Buen articulo, lo que no entiendo, es porqu ehablas de Spring Security y usas las librerias de Acegi, te tengo otra pregunta, el modelo que presentas en tu II parte, es un modelo donde, ¿solo puede existir un rol por usuario?,

    ojala escribas uno con LDAP y spring security 2.0

    saludos.

  3. 3 LeChuckNorris octubre 20, 2008 a las 20:15

    @jotadeveloper:

    Respecto a tu segunda pregunta puede haber un usuario con varios roles. Estarían contemplados por la tabla authorities. En la tabla habría tantos usernames como roles tenga. Así pues, si tengo username=pepe con authority=administrador y username=pepe con authority=usuario_plano, pues entonces pepe tendria dos roles y dado que la select devolvería datos para ambos casos:

    select USERNAME as username, authority as rolename FROM authorities WHERE username =?

    Respecto a el tema de Acegi – Spring Security supongo que es por deformación. Para mi siempre ha sido Acegi. Aunque ahora les ha dado por cambiar el nombre la API debe de ser la misma.

  4. 4 JotaDeveloper noviembre 20, 2008 a las 21:54

    mira, gracias por tu respuesta, pero te tengo otra pregunta.

    tienes algun ejemplo con hibernate? pero no hibernate con spring…

    modificando el AuthenticationProviderDao

  5. 5 Edgar Garcia febrero 5, 2009 a las 21:58

    Hola, me gusto mucho tu tutorial. Te tengo 1 pregunta.

    Yo necesito conectarme a jasper server y necesito que el usuario al dar click en un boton vaya a jasper server pero que no me pida autenticacion, que lo haga por un mecanismo “X” por codigo y que al ir a la pagina principal de jasper server me cargue sin problemas.

    Sabes que “X” es lo que necesito hacer ??

    Gracias.

  6. 6 Armando julio 30, 2009 a las 07:41

    Hola, el tutorial esta muy bien explicado

    sin ebargo apenas empiezo a con jsp y se me complica un poco he seguido paso a paso toda la primera parte y al final me han surgido un par de errores el primero al probar la aplicacion me daba el siguiente error

    Estado HTTP 404

    El recurso requerido () no está disponible

    esto lo solucione cambiando el archivo Web.xml nada mas cambie de lugar el codigo que proporcionas.
    Pero me salen algunos detalles como el que no me redirecciona del index a welcome, si entro directamente a welcome e introduzco cualquier dato corecto o incorecto me direciona a

    j_acegi_security_check.do

    y me sale el error del principio
    estoy seguro de haver hecho todo lo del tutorial paso a paso y no se cual sea el mi error que hice mal

    por favor agradeceria tu ayuda o de alguien mas que sepa que pude hacer mal gracias.

  7. 7 LuigiBuilder septiembre 9, 2009 a las 07:54

    Respecto al error, puede ser que el archivo Welcome.jsp no esta direccionando bien el form que agregaste; en el action de form quitale un punto de los dos que aparecen, osea dejalo asi:

    action=”./j_acegi_security_check.do”

    Eso me sirvio a mi.

    Salu2

  8. 8 aantelov noviembre 12, 2009 a las 06:42

    Soy nuevo en java y en spring, deseo realizar una aplicacion escritorio puedo usar este framework para esto…

    Si tienes un ejemplo de autentificacion te lo agradeceria..

    Saludos

  9. 9 JZezar noviembre 20, 2009 a las 00:56

    Hola, podrias explicar en que consiste la autenticación anonima y cuando emplearla, En el ejemplo veo que se declaran los siguiente objetos:

    -anonymousAuthenticationProvider
    -anonymousProcessingFilter

    Pero no me queda claro como funcionan y cuando utilizarlos.

    Sin más por el momento me despido y de antemano muchas gracias por la ayuda.

  10. 10 rambaldi enero 19, 2011 a las 07:28

    El ejemplo que desarrollaste esta excelente, sin embargo, no podrias actualizarlo para struts 2? seria de gran ayuda


  1. 1 Ejemplo básico de autenticación contra BBDD usando Spring Security (parte II) « Hermoso día… Trackback en agosto 14, 2008 a las 21:54

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s




Add to Technorati Favorites
Clicky Web Analytics Clicky

Flickr Photos

Aljibe

luz

C1

C1

Más fotos

A %d blogueros les gusta esto: