| Nota | ||
|---|---|---|
| ||
Si estás trabajando con una aplicación FundeWeb 1.x debes leer la guía ... |
Pasos a seguir
...
Hasta Julio de 2021 Fundeweb sólo soportaba el login CAS a través de cuenta UM. A partir de esta fecha, CAS soporta login mediante diversos mecanimos (Certificado, Cl@ave, etc.). Para poder soportar desde aplicaciones Fundeweb que CAS utilice diferentes métodos de autenticación y poder reaccionar si alguna de nuestras aplicaciones no soportara algún método indicando los soportados deberemos seguir esta guía.
Para poder aplicar estos cambios en tu aplicación
...
necesitas FundeWeb IDE 2.0
...
o 2.1
...
.
...
Si no cumples los requisitos puedes ...
Hay que modificar o añadir las siguientes carpetas y ficheros:
...
AuthenticationManagerBean.java
Añadir esta clase:
| Bloque de código | ||||||
|---|---|---|---|---|---|---|
| ||||||
package es.um.atica.apium.security.authentication;
public class AuthenticationMethodNotSupportedException extends RuntimeException {
private static final long serialVersionUID = -7140000370097499128L;
private static final String MENSAJE = "No se soporta el método de autenticación: ";
public AuthenticationMethodNotSupportedException( String message ) {
super( MENSAJE + message );
}
public AuthenticationMethodNotSupportedException( Throwable cause ) {
super( MENSAJE, cause );
}
public AuthenticationMethodNotSupportedException( String message, Throwable cause ) {
super( MENSAJE + message, cause );
}
} |
AuthenticationMethodSSO.java
- Asegurar que AuthenticationManagerBean extiende la clase AbstractAuthenticationManagerBean.
- Añadir los métodos de autenticación validos que ofrece el SSO.
Este es un ejemplo en el que solo admitimos la autenticación mediante correo del SSO para acceder a la aplicaciónAñadir esta clase que es idéntica a es.um.atica.seam.security.authentication.method.AuthenticationMethodSSO.java salvo porque sobrescribe al final el método preAuthenticate() (línea 96):
| Bloque de código | ||||||
|---|---|---|---|---|---|---|
| ||||||
package es.um.atica.apiumXXXX.security.authentication; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.Objects; import org.jboss.seam.logannotations.LogFactory; import org.jboss.seam.log.Logging; import org.jboss.seam.util.Strings; import org.umu.atica.servicios.gesper.gente.entity.Persona; 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.apiumseam.security.authentication.AuthenticationManagerBean.AuthenticationTypeConstants; import es.um.atica.seam.security.authentication.method.AuthenticationMethodSSOAuthenticationMethods; import es.um.atica.seam.utils.CasClient3Util; /** * Clase para definir el metodo de autenticación por SSO mediante CAS de la UMU. * * @author juanmiguelbg * @version 0.0.3 */ @Name( "authenticationManagerBean" ) @Scope( SESSION ) @Install( precedence = FRAMEWORK ) @BypassInterceptors @Startup public class AuthenticationMethodSSOAuthenticationManagerBean extends AuthenticationMethodAbstractAuthenticationManagerBean { private// staticDeclaracion finalde Loglos LOGmetodos = Logging.getLog(AuthenticationMethodSSO.class); de autenticacion validos por SSO private static final StringSSOAuthenticationMethods[] CLIENTVALIDS_NAME_KEY = "clientName"; private static final String AUTHENTICATION_METHOD_NAME_KEY = "authenticationMethod"; private static final String CLAVE_CLIENT_NAME = "Cl@ve"; private static final String CERT_CLIENT_NAME = "Cert"; private static final String FIRST_NAME_KEY = "FirstName"; private static final String FAMILY_NAME_KEY = "FamilyName"; /* * (non-Javadoc) * * @see es.um.atica.util.FundeWebManager#getLog() */ @Override protected Log getLog() { return LOG; } @Override public boolean authenticate() { LOG.info("Autenticando a: #0", getCredentials().getUsername()); getStatusMessages().clearGlobalMessages(); try { if ( isDni( getCredentials().getUsername() ) ) { // Autenticacion Clave - DNI loadPersonaByDniCAS( getCredentials().getUsername() ); } else { // Autenticacion Correo UMU - Ticarum loadUser( getCredentials().getUsername() ); } return true; } catch (ServiceNotFoundException snfe) { LOG.error("Error al buscar el servicio de Gente", 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; } private void loadPersonaByDniCAS( String username ) throws PersonaException, PersonaNotFoundException, ServiceNotFoundException { try { loadPersonaByIdentificador( username ); } catch ( PersonaNotFoundException pnfe ) { LOG.warn( "El usuario no se encuentra en GENTE, completamos con datos del CAS.", pnfe ); loadPersonaByDniClave( username ); } } private void loadPersonaByDniClave( String username ) { Map<String, Object> atributos = CasClient3Util.getPrincipalAttributes( CasClient3Util.getCasClient3Principal() ); LOG.info( "Atributos: #0", atributos ); String client = (String) atributos.get( CLIENT_NAME_KEY ); if ( !Strings.isEmpty( client ) && ( CLAVE_CLIENT_NAME.equals( client ) || CERT_CLIENT_NAME.equals( client ) ) ) { String nombre = new String( ( ( String ) atributos.get( FIRST_NAME_KEY ) ).getBytes(), StandardCharsets.UTF_8 ); String apellidos = new String( ( ( String ) atributos.get( FAMILY_NAME_KEY ) ).getBytes(), StandardCharsets.UTF_8 ); Persona persona = new Persona( username, nombre, apellidos, null ); getUmuIdentity().setPersona( persona ); } } @Override public void preAuthenticate() { final Map<String, Object> atributos = CasClient3Util.getPrincipalAttributes( CasClient3Util.getCasClient3Principal() ); final String credentialType = ( String ) atributos.get( AUTHENTICATION_METHOD_NAME_KEY ); final String client = ( String ) atributos.get( CLIENT_NAME_KEY ); boolean encontrado = false; for ( final AuthenticationType authType : AuthenticationType.values() ) { if ( Objects.equals( credentialType, authType.getAuthenticationMethod() ) && Objects.equals( client, authType.getClientName() ) ) { encontrado = true; break; } } if ( !encontrado ) { throw new AuthenticationMethodNotSupportedException( client ); } } } |
AuthenticationFactorySSO.java
Añadir esta clase asegurándonos que en el return del método createAuthenticationMethod() ponemos el nombre completo de la clase anterior (línea 11):
| Bloque de código | ||||||
|---|---|---|---|---|---|---|
| ||||||
package es.um.atica.apium.security.authentication;
import es.um.atica.seam.security.authentication.credentials.CredentialsDefaultUmu;
import es.um.atica.seam.security.authentication.credentials.CredentialsUmu;
import es.um.atica.seam.security.authentication.factories.AuthenticationFactory;
import es.um.atica.seam.security.authentication.method.AuthenticationMethod;
public class AuthenticationFactorySSO implements AuthenticationFactory {
public AuthenticationMethod createAuthenticationMethod() {
return new es.um.atica.apium.security.authentication.AuthenticationMethodSSO();
}
public CredentialsUmu createCredentials() {
return new CredentialsDefaultUmu();
}
} |
AuthenticationManagerBean.java
SSO_AUTHENTICATION_METHODS = {
SSOAuthenticationMethods.SSO_CORREO
};
...
// Forma de hacer accesible los metodos de autenticacion validos por SSO
@Factory( Constants.SSO_AUTHENTICATION_METHODS_COMPONENT_NAME )
public SSOAuthenticationMethods[] getValidsSSOAuthenticationMethods() {
return VALIDS_SSO_AUTHENTICATION_METHODS;
}
} |
Los métodos de autenticación posibles son:
| Bloque de código |
|---|
SOAuthenticationMethods[]: SSO_CORREO, SSO_MFA_CORREO_OTP, SSO_CLAVE, SSO_CLAVE_CERT, SSO_CLAVE_EIDAS, SSO_CLAVE_SEGSOC, SSO_CLAVE_PIN24H, SSO_CLAVE_PIN24H_MOVIL, SSO_CMN |
Añadir SSO_CORREO implica, que se aceptan SSO_CORREO y SSO_MFA_CORREO_OTP.
Añadir SSO_CLAVE implica, que se aceptan todos los métodos de Cl@ve: SSO_CLAVE_CERT, SSO_CLAVE_EIDAS, SSO_CLAVE_SEGSOC, SSO_CLAVE_PIN24H y SSO_CLAVE_PIN24H_MOVIL.
Sino se usa la anotación @Factory, podemos declarar la factoría en el fichero components.xml:
| Bloque de código |
|---|
<factory name="es.um.atica.security.authentication.ssoAuthenticationMethods" value="#{authenticationManagerBean.validsSSOAuthenticationMethods}"/> |
o
| Bloque de código |
|---|
<factory name="es.um.atica.security.authentication.ssoAuthenticationMethods" method="#{authenticationManagerBean.getValidsSSOAuthenticationMethods}"/> |
AuthenticatorAction.java
- Asegurar que AuthenticatorAction extiende la clase AbstractAuthenticatorAction.
La clase normalmente, suele parecerse al siguiente ejemploModificar esta clase haciendo que extienda a AbstractAuthenticationManagerBean en lugar de a FundeWebManagerBean (línea 37) y asegurándonos que el return del método getFactoria() en el caso SSO ponemos el nombre completo de la clase anterior (línea 119):
| Bloque de código | ||||||
|---|---|---|---|---|---|---|
| ||||||
package es.um.atica.apiumXXXX.security.authentication; import static org.jboss.seam.ScopeType.SESSION; import static org.jboss.seam.annotations.Install.FRAMEWORK; import java.io.Serializable; import java.util.MissingResourceException; import java.util.ResourceBundle; import javax.faces.model.SelectItem; import org.jboss.seam.Component; import org.jboss.seam.annotations.Install; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Observer; import org.jboss.seam.annotations.Scope; import org.jboss.seam.annotations.Startup; import org.jboss.seam.annotations.intercept.BypassInterceptors; import org.jboss.seam.contexts.Contexts; import org.jboss.seam.core.SeamResourceBundle; import org.jboss.seam.log.Log; import org.jboss.seam.log.Logging; import es.um.atica.apium.security.authentication.ws.AuthenticationFactoryCorreo; import es.um.atica.seam.security.CredentialsAdapter; import es.um.atica.seam.security.UmuIdentity; import es.um.atica.seam.security.authentication.AbstractAuthenticationManagerBean; import es.um.atica.seam.security.authentication.credentials.CredentialsUmu; import es.um.atica.seam.security.authentication.factories.AuthenticationFactory; import es.um.atica.seam.security.authentication.method.AuthenticationMethod; AbstractAuthenticatorAction; @Name( "authenticationManagerBeanauthenticator" ) @Scope( SESSION ) @Install( precedence = FRAMEWORK ) @BypassInterceptors @Startup public class AuthenticationManagerBeanAuthenticatorAction extends AbstractAuthenticationManagerBean implementsAbstractAuthenticatorAction Serializable { /** * serialVersionUID generado automaticamente */ private static final long serialVersionUID = -6064182119922723132L; /** Logger de la clase */ private static final Log LOG = Logging.getLog( AuthenticationManagerBeanAuthenticatorAction.class ); protected SelectItem[] selectItemsAutentication; /** Credencial actual */@Override protected CredentialsAdapter credentialsAdapter; protected AuthenticationType authenticationType; public enum AuthenticationType { CORREO, SSO } private static final String ERROR_FIRMA = "0"; public AuthenticationManagerBeanAbstractAuthenticationManagerBean getAuthenticationManagerBean() { // Por defecto CORREO this.credentialsAdapter = ( CredentialsAdapter ) this.getCredentials(); this.authenticationType = AuthenticationType.CORREO; this.activateCredentialsUmureturn AuthenticationManagerBean.instance(); int idx = 0; selectItemsAutentication = new SelectItem[AuthenticationType.values().length]; for ( AuthenticationType type : AuthenticationType.values() ) { selectItemsAutentication[idx++] = new SelectItem( type.name(), getAuthenticationTypeLabel( type ) ); } } public void activateCredentialsUmu() { LOG.info( "Entrar en activateCredentialsUmu: #0", this.authenticationType.name() ); this.credentialsAdapter.setCredentialsUmu( getFactoria( this.authenticationType ).createCredentials() ); } /** * Metodo para activar una credencial.<br />(non-Javadoc) * 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.@see es.um.atica.util.FundeWebManagerBean#getLog() */ public boolean activateCredentialsUmu( AuthenticationType authenticationType ) { LOG.info( "Entrar en activateCredentialsUmu: #0", ( authenticationType != null ? authenticationType.name() : "" ) ); if ( ( this.getCredentialsUmu() != null ) && ( this.authenticationType == authenticationType ) ) { if ( LOG.isDebugEnabled() ) { LOG.debug( "La credencial actual y la pedida son iguales, luego no se crear� una nueva: #0.", this.authenticationType ); } return false; } if ( authenticationType != null ) { this.setAuthenticationType( authenticationType ); } else { // Por defecto CORREO this.setAuthenticationType( AuthenticationType.CORREO ); } this.credentialsAdapter.setCredentialsUmu( getFactoria( this.authenticationType ).createCredentials() ); return true; } public AuthenticationMethod getAuthenticationMethod() { return this.getFactoria( this.authenticationType ).createAuthenticationMethod(); } /** * @param authenticationType * - parametro de Seam por defecto * @return */ protected AuthenticationFactory getFactoria( AuthenticationType authenticationType ) { if ( this.authenticationType == null ) { activateCredentialsUmu( AuthenticationType.CORREO ); } switch ( this.authenticationType ) { case SSO: // case SSO return new es.um.atica.apium.security.authentication.AuthenticationFactorySSO(); case CORREO: // case CORREO return new AuthenticationFactoryCorreo(); default: return new es.um.atica.apium.security.authentication.AuthenticationFactoryRadius(); } } protected String getAuthenticationTypeLabel( AuthenticationType authenticationType ) { ResourceBundle srb = SeamResourceBundle.getBundle(); try { switch ( authenticationType ) { case CORREO: return srb.getString( "label.tipo_acceso_correo" ); case SSO: return srb.getString( "label.tipo_acceso_sso" ); default: return srb.getString( "tipo no identificado" ); } } catch ( MissingResourceException mre ) { LOG.error( "Error al obtener las etiquetas para los tipos de autenticacion.", mre ); } return ""; } /** * Obtiene la credencial actual. */ public CredentialsUmu getCredentialsUmu() { return this.credentialsAdapter.getCredentialsUmu(); } public AuthenticationType getAuthenticationType() { return authenticationType; } public void setAuthenticationType( AuthenticationType authenticationType ) { LOG.debug( "Entra en setAuthenticationType: #0 - #1", authenticationType.hashCode(), authenticationType.name() ); this.authenticationType = authenticationType; } public boolean isCorreoAuthentication() { return this.authenticationType == AuthenticationType.CORREO; } public boolean isSsoAuthentication() { return this.authenticationType == AuthenticationType.SSO; } public SelectItem[] getSelectItemsAutentication() { return selectItemsAutentication; } @Observer( UmuIdentity.EVENT_AUTHENTICATING_BY_CAS ) public void activarAuthenticacionSSO() { LOG.debug( "Entra en activarAuthenticacionSSO" ); this.authenticationType = AuthenticationType.SSO; this.activateCredentialsUmu(); } /* * (non-Javadoc) * @see es.um.atica.util.FundeWebManager#getLog() */ @Override protected Log getLog() { return LOG; } public static AuthenticationManagerBean instance() { if ( !Contexts.isSessionContextActive() ) { throw new IllegalStateException( "no session context active" ); } return ( AuthenticationManagerBean ) Component.getInstance( AuthenticationManagerBean.class ); } public static String getErrorfirma() { return ERROR_FIRMA; } } |
AuthenticatorAction.java
Sustituir la clase completa:
@Override
protected Log getLog() {
return LOG;
}
} |
pages.xml
Añadir la siguiente regla de navegación al final de la declaración de <page view-id="*">:
| Bloque de código | ||||
|---|---|---|---|---|
| ||||
<navigation from-action="#{identity.relogByCAS}">
<redirect url="https://${cas.server.url}.um.es/cas/logout">
<param name="service" value="https://${cas.application.url}#{request.contextPath}"/>
</redirect>
</navigation> |
Añadir la siguiente excepción:
| Bloque de código | ||||||
|---|---|---|---|---|---|---|
| Bloque de código | ||||||
| ||||||
package es.um.atica.apium.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 <exception class="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 exceptions.AuthenticationMethodNotSupportedException"> <redirect view-id="/fundeweb/error_auth_method.xhtml"> <message severity="error">#{messages['es.um.atica.util.FundeWebManagerBean#getLog() */ @Override protected Log getLog() { return LOG; } } |
messages_en.properties
Añadir al final del fichero las siguientes variables.
| Bloque de código | ||||
|---|---|---|---|---|
| ||||
es.um.atica.security.authentication.AuthenticationMethodNotSupportedException=Authentication method not supported |
messages_es.properties
Añadir al final del fichero las siguientes variables.
| Bloque de código | ||||
|---|---|---|---|---|
| ||||
es.um.atica.security.authentication.AuthenticationMethodNotSupportedException=M\u00E9todo de autenticaci\u00F3n no soportado |
pages.xml
security.authentication.AuthenticationMethodNotSupportedException']}</message>
</redirect>
</exception> |
Modificar la excepción para la clase org.jboss.seam.security.AuthorizationExceptionAñadir la siguiente excepción:
| Bloque de código | ||||
|---|---|---|---|---|
| ||||
<exception class="esorg.umjboss.atica.apiumseam.security.authentication.AuthenticationMethodNotSupportedExceptionAuthorizationException"> <redirect view-id="/fundeweb/error_no_auth.xhtml"> <message severity="error">#{messages['esorg.umjboss.aticaseam.security.authentication.AuthenticationMethodNotSupportedExceptionAuthorizationException']}</message> </redirect> </exception> |
Comprobación
Es necesaria realizar la comprobación de la existencia del fichero recomendaciones.xhtml que se tiene que encontrar en la carpeta src/main/webapp/layout del módulo WEB. Sino existe la podéis descargar de recomendaciones.xhtml