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
Comentarios recientes