Posts Tagged 'ejemplo'

Ejemplo de Drag & Drop con JQuery

En el mundo javascript existe un antes y un después a las librerías tipo JQuery. Sinceramente, no se como he podido vivir sin ellas hasta ahora.
Voy a realizar un ejemplo sencillo, aunque bastante impresionante por usar eventos drag & drop, para demostrar la potencia del bicho a l@s no iniciad@s.

Ingredientes:

  • Dos listas
  • Items en cada lista 😉
  • Un botón para mostrar los datos de cada lista
  • JQuery

Objetivo: Arrastrar items de una lista a otra y mostrar la información que existe en cada una al pulsar un botón.

Primero creo un html sencillo con 2 listas:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
                    "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  <style>
  	.lista {
  		background-color:red;
  		height: 100px;
	}
  </style> 
</head>  
<body>
  
<ul class="lista" id="listaOrigen">
	<li class="item">item 1</li>
	<li class="item">item 2</li>
	<li class="item">item 3</li>
</ul>


<ul class="lista" id="listaDestino">
</ul>

</body>
</html>

Añado al head las referencias a los ficheros de JQuery. En este caso me hace falta el ui.core.js el ui.draggable.js y el ui.droppable.js además del mínimo para que funcione JQuery.

<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="http://dev.jquery.com/view/tags/ui/latest/ui/ui.core.js"></script>
<script src="http://dev.jquery.com/view/tags/ui/latest/ui/ui.draggable.js"></script>
<script src="http://dev.jquery.com/view/tags/ui/latest/ui/ui.droppable.js"></script>

*Nota: En el ejemplo uso rutas absolutas, en un entorno de producción sería conveniente descargar los ficheros y usar rutas relativas.

El siguiente paso sería hacer cada uno de los elementos cuyo class sea “item” arrastrables. Para ello añado el siguiente javascript también en la cabecera. La propiedad helper a clone indica que sea un clon del objeto a arrastrar el que se arrastre y que el original se mantenga en su sitio hasta soltarlo sobre su objetivo.

<script>
$(document).ready(function(){
   $(".item").draggable({helper: 'clone'});
});
</script>

$(document).ready(function() indica que se lance el script al cargar el documento (similar al onload).

Una vez hecho esto ya se pueden arrastrar los items. Ahora tengo que definir los destinos (droppables) que en este caso serán las dos listas dado que su atributo class es lista.
La propiedad accept indica el tipo de objeto que acepta sea arrastrado sobre el. La propiedad drop define una función que se ejecutará al soltar un objeto draggable del tipo definido en el accept. Esta función añade al objeto droppable “$(this).append($(ui.draggable))” el objeto arrastrado sobre el.

$(".lista").droppable({				
				accept: ".item",				
				drop: function(ev, ui) {									
					// Añado el objeto origen a la lista destino									
					$(this).append($(ui.draggable));					
				}
			});

Ya puedo mover objetos de una lista a otra. El siguiente paso es crear un botón que muestre los items contenidos de cada lista cuando se haga click sobre el.

Creo el botón en html…

<input type="button" id="botonResultado" value="ver datos">

… y añado una acción al evento onclick del botón que recorre los elementos li de las listas y extrae su contenido con la función html().

$("#botonResultado").click(function () {  
 var salida = "";
 $('.lista li').each(function () {
   salida += $(this).parent().attr('id') + " : " + $(this).html() + "\n";
 });
				
alert(salida);
});

*NOTA: También dentro de la función $(document).ready)

Como se puede ver, con 4 líneas de código Jquery se pueden hacer cosas chulas.

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

Continuo con la segunda parte del ejemplo comenzado ayer.

Una vez conseguida la auntenticación contra un usuario cargado en memoria por Acegi el siguiente paso sería preparar la infraestructura de base de datos contra la que se va a realizar la nueva comprobación de credenciales. Como mínimo la base de datos ha de almacenar, como es lógico, el nombre de usuario y la password de cada uno de los usuarios. Además, cada uno de ellos ha de tener un perfil asociado para en función de este y de las reglas del filtro se permita o no el acceso a determinados paths.

Desde la página oficial de Spring Security nos sugieren este modelo de BBDD (para la autenticacion basta con la tabla users y authorities):

El script de creación de la BBDD se puede encontrar en la página de Spring Security pero es una implementación para Hypersonic. Yo voy a usar MySQL así que para este ejemplo ejecutaré este otro script sacado del foro de Spring:

DROP TABLE IF EXISTS contacts;
DROP TABLE IF EXISTS authorities;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS acl_permission;
DROP TABLE IF EXISTS acl_object_identity;

CREATE TABLE users (
	username varchar(50) NOT NULL,
	password varchar(50) NOT NULL,
	enabled bool NOT NULL,
	
	constraint PK_Username primary key (username)
) ENGINE=InnoDB;


CREATE TABLE authorities (
	username varchar(50) NOT NULL,
	authority varchar(50) NOT NULL,
	
	constraint FK_Username foreign key (username) references users(username),
	constraint IX_AuthUsername unique key (username, authority)
) ENGINE=InnoDB;
        

CREATE TABLE acl_object_identity (
  	id int NOT NULL auto_increment,
  	object_identity varchar(250) NOT NULL,
  	parent_object int,
  	acl_class varchar(250) NOT NULL,
  	
  	index (parent_object),
  	
  	constraint PK_Id primary key (id),
	constraint FK_ParentObject foreign key (parent_object) references acl_object_identity(id)

) ENGINE=InnoDB;


CREATE TABLE acl_permission (
  	id int NOT NULL auto_increment,
  	acl_object_identity int NOT NULL,
  	recipient varchar(100) NOT NULL,
  	mask int NOT NULL,
  	
  	constraint PK_Id primary key (id),
	constraint FK_ACLIdentity foreign key (acl_object_identity) references acl_object_identity(id),
	constraint IX_URecipient unique key (acl_object_identity, recipient)

) ENGINE=InnoDB;

INSERT INTO users VALUES('pepe','pepe',1);

INSERT INTO authorities VALUES('pepe','ROLE_ADMINISTRADOR);

Creo la BBDD y ejecuto el script. Ahora hay que definir un datasource usando Spring. Para ello creo (por separar conceptos) un nuevo fichero de contexto que llamo applicationContext-acegi-security.xml y lo coloco en el raíz de la carpeta WEB-INF.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
        <property name="location"><value>classpath:/databaseMySQL.properties</value></property>    
    </bean>
    
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName"><value>${driver}</value></property>
		<property name="url"><value>${url}</value></property>
		<property name="username"><value>${username}</value></property>
		<property name="password"><value>${password}</value></property>
		<property name="defaultReadOnly"><value>${defaultReadOnly}</value></property>				
		<property name="maxActive"><value>${maxActive}</value></property>
		<property name="maxWait"><value>${maxWait}</value></property>
		<property name="validationQuery"><value>${validationQuery}</value></property>
	</bean>	

</beans>

Una vez creado se referencia añadiéndolo al parámetro de contexto contextConfigLocation en el web.xml que quedaría así:

<context-param>
		<param-name>contextConfigLocation</param-name>

		<param-value>
			/WEB-INF/applicationContext-acegi-security.xml /WEB-INF/applicationContext-acegi-datasource.xml
		</param-value>
	</context-param>

El jar de Commons BDCP se puede descargar href=”http://commons.apache.org/downloads/download_dbcp.cgi”>aquí. También hace falta el Commons Pool. Por supuesto también es necesario añadir el driver JDBC de MySQL al directorio de librerias.

También hay que crear el fichero .properties del que obtiene los parámetros: databaseMySQL.properties.

driver=com.mysql.jdbc.Driver
username=*****
password=*****
url=jdbc:mysql://localhost:3306/pruebas
initialSize=5
maxActive=5
maxWait=500
maxIdle=3
minIdle=1
validationQuery=SELECT 'ok' from dual
testOnBorrow=true
defaultReadOnly=false

El siguiente paso es añadir el siguiente fragmento de código al fichero applicationContext-acegi-security.xml. En el se hace referencia al datasource creado previamente y se pasan dos parámetros clave para la autenticación:

  • usersByUsernameQuery: SQL para obtener los datos del usuario. Lo que espera obtener Acegi es el username la password y si el usuario es enabled o no (los as)
  • authoritiesByUsernameQuery:SQL para obtener el ROL del usuario obtenido de la query del parámetro usersByUsernameQuery
<bean id="authenticationDao"
	      class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl">
		<property name="dataSource">
			<ref bean="dataSource"/>
		</property>
		
	<property name="usersByUsernameQuery">
			<value>select USERNAME as username, PASSWORD as password, ENABLED as enabled FROM users WHERE username =? </value>
			</property>
		<property name="authoritiesByUsernameQuery">
		<value>select USERNAME as username, authority as rolename FROM authorities WHERE username =? </value>
		</property>
	</bean>

Y ya está. Ya se puede probar. Tan sólo quedan un par de detalles por zanjar: la encriptación de la password.

Añadir la propiedad passwordEncoder a el bean daoAuthenticationProvider:

    	<property name="passwordEncoder">
      		<ref bean="passwordEncoder" />
    	</property>

y el correspondiente bean, en este caso utilizado Md5 como algoritmo de encriptación:

<bean id="passwordEncoder"
   		class="org.acegisecurity.providers.encoding.Md5PasswordEncoder"/>

Aquí se puede encontrar el javadoc del paquete encoding.

—- Actualización: (18 Agosto de 2008)

La información del usuario logueado se guarda en el atributo de sesión ACEGI_SECURITY_CONTEXT. A partir de este atributo se puede obtener el login del usuario. Yo, lo que hago normalmente en una aplicación real, es sustituir lo que en el ejemplo he llamado EstoyDentro.do por un action, que a partir del login validado por Acegi, consulte el resto en BBDD (nombre, apellido, etc) y luego lo meta en sesión. Posteriormente hago un forward a donde corresponda.

Este es un snippet de como obtener el login:

SecurityContext contexto = 
(SecurityContext)request.getSession().getAttribute("ACEGI_SECURITY_CONTEXT");

String login = ((org.acegisecurity.userdetails.User)
							(context.getAuthentication().getPrincipal())).getUsername();

Y esto es todo amigos… próximamente en Hermosodía:

  • Ejemplo de TestNG
  • Histórico de BBDD

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


Add to Technorati Favorites
Clicky Web Analytics Clicky

Flickr Photos