====== Autenticación en Fundeweb 2.0 ======
--- //[[pedrody@um.es|PEDRO DELGADO YARZA]] 2014/01/29 13:51//
Es obligatorio realizar las modificaciones indicadas en la siguiente guía:
[[:fdw2.0:fundeweb2.0:gt:actualizacion-sistema-autenticación|Actualización del Sistema de Autenticación de Aplicaciones FundeWeb]]
===== Mecanismo de Autenticación =====
En Fundeweb 2.0 se ha dispuesto de un mecanismo de **autenticación múltiple** que dispone de una serie de métodos de autenticación implementados por defecto y nos permite añadir nuevos mecanismos para ampliar la funcionalidad del componente en caso de ser necesario.
Los mecanismos existentes por defecto son los siguientes:
* Autenticación por NIF: Tenemos el usuario (DNI/NIF/NIE) y contraseña, y tenemos que verificarlos con algo realizado por nosotros.
* Autenticación por CAS: Utilizamos el servidor Single Sing On para validar a nuestro usuario.
* Autenticación por CERTIFICADO: Utilizamos un certificado que acredite al usuario.
* Autenticación por TARJETA: Leemos del chip de la tarjeta las credenciales del usuario y comprobamos que el login es válido.
* Autenticación por RADIUS: Utilizamos el servidor RADIUS para verificar las credenciales del usuario.
Este mecanismo se dispone en la clase **//AuthenticationManagerBean//** (que tiene que extender la clase **//AbstractAuthenticationManagerBean//**) en la cual según el método de autenticación que hayamos seleccionado, se lanzan las librerías necesarias para realizar la autenticación.
El siguiente código es el encargado de obtener la factoría de autenticación necesaria en cada caso.
protected AuthenticationFactory getFactoria(AuthenticationType authenticationType) {
if (authenticationType == null) {
activateCredentialsUmu( this.defaultAuthenticationType );
}
switch (authenticationType) {
case CARD: {
return new AuthenticationFactoryCard();
}
case CERTIFICATE: {
return new AuthenticationFactoryCertificateSSL();
}
case NIF: {
return new AuthenticationFactoryNif();
}
case RADIUS: {
return new AuthenticationFactoryRadius();
}
case SSO: {
return new AuthenticationFactorySSO();
}
default: {
return this.defaultAuthenticationFactory;
}
}
}
La estructura de una factoría consta de dos métodos:
* //createAuthenticationMethod//: Que nos devuelve la clase encargada de autenticar al usuario.
* //createCredentials//: Que nos devuelve la clase encargada de gestionar las credenciales del usuario.
El código de la factoría quedaría así:
public class AuthenticationFactoryRadius implements AuthenticationFactory {
public AuthenticationMethod createAuthenticationMethod() {
return new AuthenticationMethodRadius();
}
public CredentialsUmu createCredentials() {
return new CredentialsDefaultUmu();
}
}
===== Añadir nuevo método de autenticación =====
Si deseáramos ampliar la funcionalidad de Fundeweb 2.0 añadiendo un nuevo método de autenticación deberemos realizar lo siguiente:
* En la clase AuthenticationManagerBean añadir el nuevo mecanismo de autenticación, para ello debemos:
* Ampliar el Enumerado **AuthenticationType** con el identificador del nuevo mecanismo de autenticación.
public enum AuthenticationType {
RADIUS, CARD, CERTIFICATE, NIF, SSO
}
* Añadir el label que queremos que se muestra al usuario modificando el método **getAuthenticationTypeLabel**
protected String getAuthenticationTypeLabel(AuthenticationType authenticationType) {
ResourceBundle srb = SeamResourceBundle.getBundle();
try {
switch (authenticationType) {
case CARD: {
return srb.getString("label.tipo_acceso_tarjeta");
}
case CERTIFICATE: {
return srb.getString("label.tipo_acceso_certificado");
}
case NIF: {
return srb.getString("label.tipo_acceso_nif");
}
case RADIUS: {
return srb.getString("label.tipo_acceso_correo");
}
case SSO: {
return srb.getString("label.tipo_acceso_sso");
}
default:
return this.defaultAuthenticationTypeLabel;
}
} catch (MissingResourceException mre) {
LOG.error("Error al obtener las etiquetas para los tipos de autenticacion.", mre);
}
return "";
}
* Crear la correspondiente factoría de autenticación e incluirla en el método **getFactoria**
* Crear una clase con el mecanismo de autenticación que herede de **//AuthenticationMethod//** e implementar los métodos:
* //preAuthenticate//: su misión es establecer los datos en la credencial de autenticación. No debe de lanzar ninguna excepción.
* //authenticate//: realiza la autenticación en si, indicando con un valor booleano el sentido de esta. No establecer el valor del //username// en la credencial.
* //postAuthenticate//: su misión es poder ejecutar código, inmediatamente después de la autenticación y antes de que se lancen los eventos "org.jboss.seam.security.loginSuccessful" y "org.jboss.seam.security.loginFailed". No debe de lanzar ninguna excepción.
@Override
public void preAuthenticate() {
try {
// Vuestro código para asignar los datos a la credencial
getIdentity().getCredentials().setUsername("miusuario@um.es");
} catch (Throwable t) {
LOG.error("Error inesperado.", t);
}
}
@Override
public boolean authenticate() {
String usuario = getIdentity().getCredentials().getUsername();
LOG.info("Autenticando a: #0", usuario);
if (!UtilString.esCadenaVacia(usuario) && !usuario.contains("@")) {
usuario += "@um.es";
}
if ("invitado@um.es".equals(usuario) && "bienvenido".equals(getIdentity().getCredentials().getPassword())) {
return true;
}
try {
.....
.....
.....
.....
} catch (ServiceNotFoundException snfe) {
LOG.error("Error al buscar un servicio", snfe);
} catch (PersonaException pe) {
LOG.error("Error: al obtener los datos del Usuario en GENTE.", pe);
processErrorMessage();
} catch (PersonaNotFoundException pnfe) {
LOG.error("Error: el usuario no se encuentra en GENTE.", pnfe);
processErrorMessage();
}
return false;
}
@Override
public void postAuthenticate() {
try {
// Vuestro código para realizar acciones después de la autenticación
// y antes de lanzarse los eventos "org.jboss.seam.security.loginSuccessful"
// o "org.jboss.seam.security.loginFailed"
} catch (Throwable t) {
LOG.error("Error inesperado.", t);
}
}
* Crear clase de gestión de credenciales que herede de **//CredentialsUmu//** o utilizar/heredar alguna de las credenciales ya creadas, los criterios serian los siguientes:
* Si guardamos el nombre de usuario y contraseña, podemos utilizar la clase **//CredentialsDefaultUmu//**.
* Si guardamos el nombre de usuario y no la contraseña, podemos utilizarr la clase **//CredentialsDefaultNoPasswordUmu//**.
* Si guardamos los datos de otra forma, siempre hay que devolver un **//username//**, podéis ver como ejemplo la clase **//CredentialsCertificateSSLUmu//**, que almacena los datos de la credencial en el objeto //InfoCertificado// y sobrescribe el método //getUsername()// para obtener el nombre de usuario de //InfoCertificado//.
* En la factoría creada anteriormente hacer que los métodos **//createAuthenticationMethod//** y **//createCredentials//** devuelvan las nuevas clases creadas.
Tras estos pasos ya tendremos registrado y listo para usar nuestro nuevo mecanismo de autenticación.
También podemos extender o modificar algunos de los sistemas de autenticación ya creados.
==== Autenticación Personalizada ====
Podemos personalizar los métodos de autenticación y las credenciales que estan disponibles por defecto en las aplicaciones FundeWeb.
En la factoria correspondiente al sistema de autenticación, aparecen las constantes para designar los nombres de el método de autenticación y la credencial. A continuación un ejemplo para personalizar el método de autenticación con //NIF//.
\\
\\
=== Autenticación con NIF Personalizada ===
\\
Podemos personalizar la autenticación con NIF (DNI/NIF/NIE) siguiendo los siguientes pasos:
* La librería FundeWeb Seam Componentes tiene que tener las versión 2.0.76 o posterior.
* Tenemos que crear el componente //_APLICACION_AuthenticationMethodNif// en el paquete //es.um.atica._aplicacion_.security.authentication.method//.
* El componente tiene que tener el nombre indicado en la constante //AuthenticationFactoryNif.AUTHENTICATION_METHOD_NIF_COMPONENT_NAME//.
* Le añadimos la anotación //@BypassInterceptors// a la clase.
* La clase del componente tiene que extender la clase abstracta //es.um.atica.seam.security.authentication.method.AuthenticationMethod//.
* La clase del componente tiene que implementar el método //public boolean authenticate()// donde esta la lógica de autenticación que verifica las credenciales del usuario.
* La clase del componente tiene que implementar el método //protected void loadUser(Object identifier)// donde se carga la información personal del usuario, no se cargan roles ni permisos.
Código de ejemplo:
package es.um.atica.prueba.security.authentication.method;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.log.Log;
import org.jboss.seam.log.Logging;
import org.umu.atica.servicios.gesper.gente.exceptions.PersonaException;
import org.umu.atica.servicios.gesper.gente.exceptions.PersonaNotFoundException;
import buscador.servicios.exceptions.ServiceNotFoundException;
import es.um.atica.seam.security.authentication.factories.AuthenticationFactoryNif;
import es.um.atica.seam.security.authentication.method.AuthenticationMethod;
@Name(AuthenticationFactoryNif.AUTHENTICATION_METHOD_NIF_COMPONENT_NAME)
@BypassInterceptors
public class PruebaAuthenticationMethodNif extends AuthenticationMethod {
private static final Log LOG = Logging.getLog(PruebaAuthenticationMethodNif.class);
@Override
public boolean authenticate() {
try {
this.loadUser( getCredentials().getUsername() );
LOG.info("Autenticando a: #0 - #1", getCredentials().getUsername(),
getUmuIdentity().getPersona().getCorreo());
// TODO logicapara la verificacion de las credenciales de autenticacion
return ...;
} catch (ServiceNotFoundException snfe) {
LOG.error("Error al buscar un servicio", snfe);
} catch (PersonaException pe) {
LOG.error("Error: al obtener los datos del Usuario en GENTE.", pe);
processErrorMessage();
} catch (PersonaNotFoundException pnfe) {
LOG.error("Error: el usuario no se encuentra en GENTE.", pnfe);
processErrorMessage();
} catch ( Throwable t ) {
LOG.error("Error inesperado.", t);
}
return false;
}
@Override
protected void loadUser(Object identifier) throws Exception {
// TODO Cargar los datos del usuario (no permisos)
// Este método carga los datos de la persona en base al DNI de Gente
this.loadPersonaByIdentificador( (String) identifier );
}
/*
* (non-Javadoc)
* @see es.um.atica.util.FundeWebManager#getLog()
*/
@Override
protected Log getLog() {
return LOG;
}
}
Para otros métodos de autenticación, hay que utilizar la factoria relacionada.