====== 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: * Abrimos el POM Principal del proyecto y modificamos el //// a la versión [[https://archiva.um.es/archiva/#artifact~internal/es.um.atica.fundeweb/parent|2.0.20 o posterior]] (para aplicaciones FundeWeb 1.5 la versión [[https://archiva.um.es/archiva/#artifact~internal/org.um.atica.fundeweb/seam-parent/|1.5.22 o posterior, ignorar si se esta migrando a WLS 12.2]]). * Posiblemente, tengamos problemas de dependencias si estabamos en una version del //// anterior a la 2.0.17. Para solucionar el problema, abrimos el POM del módulo WEB (para la aplicaciones FundeWeb 1.5, es en el módulo EJB) y eliminamos las dependencias //cas-client-core// y //velocity// y añadimos las siguiente en su lugar: org.jasig.cas.client cas-client-support-saml * Abrimos el fichero //weblogic-applicaction.xml// y añadimos dentro de la etiqueta //// la etiqueta //org.hibernate.validator.*//. === 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.
* 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. * {{ :fdw2.0:fundeweb2.0:gt:autenticacion_certificado:clases-autenticacion-por-defecto-fw1.zip | Clases para la autenticación por defecto de FundeWeb 1.5.x }}. Se tienen que añadir al paquete //org.umu.atica.seam.security.authentication//. * {{ :fdw2.0:fundeweb2.0:gt:autenticacion_certificado:clases-autenticacion-por-defecto-fw2.zip | Clases para la autenticación por defecto de FundeWeb 2.0.x }}. Se tienen que añadir al paquete //es.um.atica.seam._aplicacion_.security.authentication//. === Solo para Aplicaciones FundeWeb 1.5 === * Tenéis que eliminar las clases previas de la aplicación, que suelen estar en el paquete **//org.umu.atica.security.authentication//**. * Si la aplicación tiene la clase **//UmuIdentity//**, esta tiene que renombrarse (patrón //_Aplicacion_Identity//) y extender la clase **//org.umu.atica.seam.security.UmuIdentity//** de la librería //FundeWeb Seam Components//. Y debe de contener solo el código que sea diferente al de la clase **//UmuIdentity//** de la librería. * La clase **//IdentityLoader//**, esta tiene que renombrarse (patrón //_Aplicacion_IdentityLoader//) y extender la clase **//org.umu.atica.seam.security.UmuIdentityLoader//** de la librería //FundeWeb Seam Components//. Y debe de contener solo el código que sea diferente al de la clase **//UmuIdentityLoader//** de la librería. ==== 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. ---- --- //[[juanmiguelbg@um.es|JUAN MIGUEL BERNAL GONZALEZ]] 2016/04/25 11:40//