Tabla de Contenidos

Actualización del Sistema de Autenticación

Esta actualización es necesaria en aquellas aplicaciones FundeWeb 2.0 que se hayan creado con arquetipos anteriores a al versión 0.0.7 (y para todas las aplicaciones FundeWeb 1.5).

Las modificaciones se realizan sobre las clases AuthenticationManagerBean y AuthenticatorAction. Si no tienes alguna de estas clases, en la parte inferior de esta guía hay un enlace para descargarlas.

Configuración de la Aplicación

Tenemos que realizar las siguientes modificaciones:

                <dependency>
			<groupId>org.jasig.cas.client</groupId>
			<artifactId>cas-client-support-saml</artifactId>
		</dependency>

El Componente AuthenticationManagerBean

En este componente tenemos que configurar la autenticación con certificado cliente.

Primero tenemos que hacer que extienda de la clase AbstractAuthenticationManagerBean (que ya extiende la clase FundeWebManagerBean e implementa la interface Serializable) y añadir la anotación @Override sobre el método getAuthenticationMethod().

Añadimos antes de la definición de la propiedad authenticationMethod las siguientes definiciones:

    // Por defecto RADIUS
    protected final AuthenticationType defaultAuthenticationType = AuthenticationType.RADIUS;
    protected final AuthenticationFactory defaultAuthenticationFactory = new AuthenticationFactoryRadius();
    protected final String defaultAuthenticationTypeLabel =
            SeamResourceBundle.getBundle().getString("label.tipo_acceso_correo");
 
    protected AuthenticationMethod authenticationMethod;

Estas definiciones, establecen el tipo de autenticación por defecto para la aplicación, la factoría y la etiqueta por defecto para el tipo de autenticación por defecto.

Los valores son para la autenticación por Radius, si la autenticación por defecto de la aplicación es otra, cambiar los valores por los apropiados.

Modificamos el método activateCredentialsUmu(AuthenticationType authenticationType) con el siguiente contenido:

    /**
     * Método para activar una credencial.<br />
     * Si la que se desea activar, es la que está actualmente, no se hace nada y se devuelve false. En otro caso se
     * devolverá true.
     * 
     * @param credencial
     *            Clase de Credencial a activar.
     * @return Si => se creo una nueva credencial. No => ya estaba esa misma credencial activa.
     */
    protected boolean activateCredentialsUmu(AuthenticationType authenticationType) {
        this.authenticationMethod = null;
        AuthenticationType auxAuthenticationType = authenticationType;
        if (auxAuthenticationType == null) {
            auxAuthenticationType = this.defaultAuthenticationType;
        }
 
        LOG.info("Entrar en activateCredentialsUmu: #0", auxAuthenticationType.name());
        this.setAuthenticationType( auxAuthenticationType );
        this.credentialsAdapter.setCredentialsUmu( getFactoria( auxAuthenticationType ).createCredentials() );
        return true;
    }

Modificamos el método activateCredentialsUmu() con el siguiente contenido:

	public void activateCredentialsUmu() {
		activateCredentialsUmu( this.getAuthenticationType() );
	}

Modificamos el método getAuthenticationMethod() con el siguiente contenido:

    @Override
    public AuthenticationMethod getAuthenticationMethod() {
        if ( this.authenticationMethod == null ) {
            this.authenticationMethod = this.getFactoria(this.getAuthenticationType()).createAuthenticationMethod();
        }
        return this.authenticationMethod;
    }

Vamos al método protected AuthenticationFactory getFactoria(AuthenticationType authenticationType) y lo sustituimos por el siguiente:

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

Vamos al método protected String getAuthenticationTypeLabel(AuthenticationType authenticationType) y lo sustituimos por el siguiente:

    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 "";
    }

Ahora tenemos que añadir el método para activar la autenticación con certificado cliente, que se utiliza en la página de configuración anterior:

    public void activarAuthenticacionCertificadoSSL() {
        LOG.debug( "Entra en activarAuthenticacionCertificadoSSL" );
        this.activateCredentialsUmu(AuthenticationType.CERTIFICATE);
    }

Comprobamos que el método activarAuthenticacionSSO() tenga la anotación @Observer(UmuIdentity.EVENT_AUTHENTICATING_BY_CAS).

    @Observer(UmuIdentity.EVENT_AUTHENTICATING_BY_CAS)
    public void activarAuthenticacionSSO() {
        LOG.debug("Entra en activarAuthenticacionSSO");
        this.activateCredentialsUmu(AuthenticationType.SSO);
    }

Al final del fichero, añadimos el método instance():

    public static AuthenticationManagerBean instance() {
        if (!Contexts.isSessionContextActive()) {
            throw new IllegalStateException("no session context active");
        }
        return (AuthenticationManagerBean) Component.getInstance(AuthenticationManagerBean.class);
    }

Tenemos que revisar que estén los siguientes métodos (sino están, se añaden):

	public boolean isRadiusAuthentication() {
		return this.getAuthenticationType() == AuthenticationType.RADIUS;
	}
 
	public boolean isCardAuthentication() {
		return this.getAuthenticationType() == AuthenticationType.CARD;
	}
 
	public boolean isCertificateAuthentication() {
		return this.getAuthenticationType() == AuthenticationType.CERTIFICATE;
	}
 
	public boolean isNifAuthentication() {
		return this.getAuthenticationType() == AuthenticationType.NIF;
	}
 
	public boolean isSsoAuthentication() {
		return this.getAuthenticationType() == AuthenticationType.SSO;
	}

Puesto que ya no vamos a utilizar el CryptoApplet para la autenticación con certificado, podemos eliminar los siguientes métodos:

	/* ----------------- AUTENTICACION CON CERTIFICADO --------------------- */
	/** 	 * Método para obtener el XML con el Reto 	 *  	 * @return XML con el Reto 	 */
	public String getRetoXmlFuente() {
		LOG.debug("Entra en getRetoXmlFuente");
		return ((CredentialsCertificateUmu) this.getCredentialsUmu()).getRetoXml();
	}
 
	/** 	 * Método para actualizar el XML con el Reto 	 *  	 * @param retoXml 	 *            XML con el Reto a cargar 	 */
	public void setRetoXmlFuente(String retoXmlFuente) {
		LOG.debug("Entra en setRetoXmlFuente");
		((CredentialsCertificateUmu) this.getCredentialsUmu()).setRetoXml(retoXmlFuente);
	}
 
	public String getRetoXmlFirmado() {
		return ((CredentialsCertificateUmu) this.getCredentialsUmu()).getRetoXmlFirmado();
	}
 
	public void setRetoXmlFirmado(String retoXmlFirmado) {
		((CredentialsCertificateUmu) this.getCredentialsUmu()).setRetoXmlFirmado(retoXmlFirmado);
	}
 
	public String getErrorFirma() {
		return "0";
	}
 
	public void setErrorFirma() {
 
	}

La recomendación es obtener las clases de los ZIP y rehacer posteriormente los cambios que teníamos en nuestra aplicación. Que no suelen ser muchos.

El Componente AuthenticatorAction

En este componente tenemos que configurar los métodos preAuthenticate y postAuthenticate.

Primero tenemos que hacer que extienda de la clase AbstractAuthenticatorAction (que ya extiende la clase FundeWebManagerBean) y añadir las anotaciones @Install(precedence = FRAMEWORK) y @BypassInterceptors después de la anotación @Name(“authenticator”). Se pueden eliminar el resto de los métodos ya que están en la clase abstracta padre.

Si se ha modificado algún método (el más habitual es el método hasApplicationSpecificPermission()) hay que dejarlo añadiéndole la anotación @Override.

package es.um.atica.logatica.security.authentication;
 
import static org.jboss.seam.annotations.Install.FRAMEWORK;
 
import org.jboss.seam.annotations.Install;
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 es.um.atica.seam.security.authentication.AbstractAuthenticationManagerBean;
import es.um.atica.seam.security.authentication.AbstractAuthenticatorAction;
 
@Name("authenticator")
@Install(precedence = FRAMEWORK)
@BypassInterceptors
public class AuthenticatorAction extends AbstractAuthenticatorAction {
 
	private static final Log LOG = Logging.getLog(AuthenticatorAction.class);
 
	@Override
	protected AbstractAuthenticationManagerBean getAuthenticationManagerBean() {
		return AuthenticationManagerBean.instance();
	}
 
	/*
	 * (non-Javadoc)
	 * 
	 * @see es.um.atica.util.FundeWebManagerBean#getLog()
	 */
	@Override
	protected Log getLog() {
		return LOG;
	}
 
}

La clase AbstractAuthenticatorAction también observa los eventos Identity.EVENT_PRE_AUTHENTICATE y Identity.EVENT_POST_AUTHENTICATE que son los que lanzan la ejecución de los métodos preAuthenticate y postAuthenticate de las clases que extienden la clase AuthenticationMethod.

Para hacerlo más rápido, a continuación están disponibles las clases AuthenticatorAction y AuthenticationManagerBean por defecto.

Solo para Aplicaciones FundeWeb 1.5

Sobre los nuevos sistemas de Autenticación

Cuando creais nuevos metodos de autenticación o modificáis alguno existente, es muy importante que aquellas clases que implementen la interfaz AuthenticationMethod o extiendan algún otro tipo de método de autenticación, si se sobrescriben los métodos preAuthenticate o postAuthenticate, entonces en la primera línea del método sobrescrito, debe aparecer el correspondiente super.preAuthenticate() o super.postAuthenticate. Veamos algunos ejemplos:

    @Override
    public void preAuthenticate() {
        super.preAuthenticate()
        try {
            // Vuestro código para asignar los datos a la credencial
 
        } catch (Throwable t) {
            LOG.error("Error inesperado.", t);
        }
    }
 
    @Override
    public void postAuthenticate() {
        super.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);
        }
    }

Ya que esos métodos los utilizamos para poder llevar acabo acciones globales en la autenticación y es necesario respetar esta regla.

También es importante destacar, que la función del método preAuthenticate, es establecer el username en la credencial correspondiente y no establecerse en el método authenticate como hasta ahora se hacía.


JUAN MIGUEL BERNAL GONZALEZ 2016/04/25 11:40