Autenticación en Fundeweb 2.0

PEDRO DELGADO YARZA 2014/01/29 13:51

Es obligatorio realizar las modificaciones indicadas en la siguiente guía:

Actualización del Sistema de Autenticación de Aplicaciones FundeWeb

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();
	}
 
    }

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.

  • fdw2.0/fundeweb2.0/gt/como_anadir_autenticacion_multiple.txt
  • Última modificación: 11/03/2020 08:17
  • por JUAN MIGUEL BERNAL GONZALEZ