Spring Security 2, la configuración a la medida con DAO Hibernate IV

Tags: , , ,

Más filtros de los que explicamos en el artículo anterior se puede usar en Spring Security, donde vimos los filtros esenciales para la configuración de nuestra capa de seguridad.

AnonymousProcessingFilter

Este filtro es muy util, nos permite darle acceso a un usuarios anónimos a secciones del sistema, digamos que la pagina de registro debe ser de acceso anónimo, en el FilterChainProxy agregamos otra linea donde colocamos el recurso y le asignamos el filtro, ese recurso sera accesible siempre y cuando en el FilterSecurityInterceptor también podemos hacer uso del Rol que indicamos que seran Roles para identificar al usuario anónimo.

<bean id="anonymousProcessingFilter"
		class="org.springframework.security.providers.anonymous.AnonymousProcessingFilter">
		<property name="key">
			<value>MU7kyU0he1MvXEDZ9Mdj7MVvkXOXJ8uRgtg/Xb/3eJyW0HZa3csBoyvinGEC4vmi</value>
		</property>
		<property name="userAttribute">
			<value>anonymousUser,ENCUESTAME_ANONYMOUS
			</value>
		</property>
	</bean>

ExceptionTranslationFilter

Maneja cualquier AccessDeniedException y AuthenticationException arrojados dentro de la cadena de filtro. Este filtro es necesario, ya que proporciona el puente entre las excepciones de Java y las respuestas HTTP. Sólo se ocupan de mantener la interfaz de usuario. Este filtro no hace ninguna garantía real de la ejecución. Para utilizar este filtro, es necesario especificar las siguientes propiedades:

  • authenticationEntryPoint indica que el controlador debe comenzar el proceso de autenticación si un AuthenticationException se detecta. Tenga en cuenta que esto también puede cambiar el actual protocolo de http a https para un inicio de sesión SSL.
  • portResolver (opcional) Se utiliza para determinar el “verdadero” puerto por la que fue recibida la petición.
    • <bean id="exceptionTranslationFilter"
      		class="org.springframework.security.ui.ExceptionTranslationFilter">
      		<property name="authenticationEntryPoint">
      			<ref local="authenticationEntryPoint" />
      		</property>
      	</bean>

      BasicProcessingFilterEntryPoint

      Este filtro es responsable de la tramitación de cualquier solicitud que tiene un encabezado de petición HTTP Authorization con un sistema de autenticación Basica y una codificación username:password simbólico. Por ejemplo, para autenticar el usuario “Aladdin”, con contraseña “ábrete Sésamo” el siguiente encabezado se mostrara como el siguiente ejemplo, (Autorización: Básica QWxhZGRpbjpvcGVuIHNlc2FtZQ == ). Si la autorización es satisfactoria, el objetoAuthentication se colocará en el SecurityContextHolder.

      <bean id="authenticationEntryPoint" class="org.springframework.security.ui.basicauth.BasicProcessingFilterEntryPoint">
        		<property name="realmName"><value>EmForge</value></property>
      </bean>

      LogoutFilter

      Se necesita una forma de cerrar sesión, el LogoutFilter es la solución perfecta, indicamos en el constructor del bean el lugar donde seremos dirigidos cuando cerremos la sesión, y una lista de beans o manejadores de Cerrar sesión, por defecto se debe usar el SecurityContextLogoutHandler, aunque puedes personalizar el tuyo propio.

      <bean id="logoutFilter" class="org.springframework.security.ui.logout.LogoutFilter">
      	 <constructor-arg value="/pages/index.me" />
      		<constructor-arg>
      			<list>
      				<!--  <ref bean="rememberMeServices"/>  -->
      				<bean
      					class="org.springframework.security.ui.logout.SecurityContextLogoutHandler" />
      			</list>
      		</constructor-arg>

      AuthenticationProcessingFilterEntryPoint

      AuthenticationProcessingFilterEntryPoint, es el bean que nos redirecionará a la pantalla de la aplicación donde se inicia sesión, o a donde nosotros queramos por eso la propiedad loginFormUrl. En el caso en que los credenciales no sean correctos se utliza el objeto AccessDeniedHandlerImpl, y en su propiedad errorPage se le especifica a que camino se debe dirigir la aplicación en caso de error.

      <bean id="formEntryPoint" class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint">
            	<property name="loginFormUrl" value="/login.faces"/>
         	</bean>

      Estos son los filtros más usados y que podemos aplicar a nuestra seguridad, junto con los explicados anteriormente son una magnifica opción para crear nuestra seguridad, y como ven, con muy poca programación. En el siguiente artículo veremos la integración del OPENID en Spring Security.

Spring Security 2, la configuración a la medida con DAO Hibernate III

Tags: , , , , ,

El dia de hoy vamos a continuar con los Filtros, es la parte central de la seguridad de Spring, y por eso la hace tan flexible, porque esta completamente separada del modelo de Negocio, eso es lo que lo hace tan genial, pues si el dia de mañana se te ocurre cambiar toda la seguridad, tu sistema no sufrirá grandes cambios.

Los filtros se encargan de la seguridad de la aplicación. Existen tres filtros fundamentales se encadenan juntos mediante un objeto llamado “filterChainProxy”, que crea e inicializa los tres filtros; como se ve en el siguiente diagrama.

filtrosacegi

  1. El filtro AuthenticationProcessingFilter maneja la petición o requerimiento (request) que chequea la autenticación -Authentication Request Check- (“el login de la aplicación”). Para ello usa el AuthenticationManager que vimos en el articulo anterior.
  2. El filtro HttpSessionContextIntegrationFilter mantiene el objeto Authentication entre varios requests y se lo pasa al AuthenticationManager y al AccessDecisionManager cuando sea necesario.
  3. El filtro ExceptonTranslationFilter verifica la existencia de autenticación , maneja las excepciones de seguridad y ejecuta la acción apropiada. El ExceptonTranslationFilter depende del filtro siguiente, FilterSecurityInterceptor.
  4. FilterSecurityInterceptor controla el acceso restricto a recursos determinados , y el chequeo de autorización.  Conoce qué recursos son seguros y qué roles tienen acceso a ellos.  FilterSecurityInterceptor usa el AuthenticationManager y el AccessDecisionManager para hacer su trabajo.

Cuando inicias, todo esto es una maraña de filtros sin sentido, pero vamos a profundizar un poco para que te quede más claro la funcion y configuración de cada uno de ellos.

AuthenticationProcessingFilter

El primer filtro donde pasa el RequestHTTP. Este filtro se especializa en manejar el request de autentificación, valida el usuario y la contraseña, más alla de esto solo debes conocer otros parametros imporantes.

  • authenticationFailureUrl: En el caso de fallo, algún lugar debe de ir cuando no se logea el usuario.
  • defaultTargetUrl: Es el URL por defecto, generalmente es la raiz.
  • filterProcessesUrl: Es a quien le encarga la responsbilidad de verificar si el usuario se logea o no..
<bean id="authenticationProcessingFilter"
		class="org.springframework.security.ui.webapp.AuthenticationProcessingFilter">
		<property name="authenticationManager">
			<ref bean="authenticationManager" />
		</property>
		<property name="authenticationFailureUrl">
			<value>/login.me</value>
		</property>
		<property name="defaultTargetUrl">
			<value>/</value>
		</property>
		<property name="filterProcessesUrl">
			<value>/j_spring_security_check</value>
		</property>
	</bean>

HttpSessionContextIntegrationFilter

El HttpSessionContextIntegrationFilter es fácil de configurar. Su única función, es propagar por el Contexto de Seguridad la autenticación a través de todas las solicitudes. No necesita propiedades ni nada en especial.

<bean id="httpSessionContextIntegrationFilter"
		class="org.springframework.security.context.HttpSessionContextIntegrationFilter" />

ExceptionTranslationFilter

El filtro ExceptionTranslationFilter intercepta cualquier error de autenticación o autorización, por ejemplo UsernameNotFoundException o DataAccessException.

Si la excepción fue causada por una excepción de autorización lanzada por el filtro FilterSecurityInterceptor (puede ser porque no tiene permisos para acceder a un Recurso Web, una imagen o un URL), el filtro lanzará un HTTP 403 al navegador, el cual mostrará una página de acceso no autorizado.

<bean id="formExceptionTranslationFilter"
		class="org.springframework.security.ui.ExceptionTranslationFilter">
		<property name="authenticationEntryPoint">
			<ref local="formEntryPoint" />
		</property>
	</bean>
 
<bean id="formEntryPoint"
		class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint">
		<property name="loginFormUrl" value="/login.me" />
	</bean>

FilterSecurityInterceptor

El FilterSecurityInterceptor, es donde protegeremos todos nuestros recursos, donde decidimos que ROL entra a ciertos recursos, cuales pueden ser accedido por usuarios anónimos, todo esto se configura en el objectDefinitionSource. Necesitamos 2 referencias para configurar este Filtro, el authenticationManager y el bean accessDecisionManager que miraremos en el siguiente artículo.

<bean id="filterInvocationInterceptor"
		class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
		<property name="authenticationManager" ref="authenticationManager" />
		<property name="accessDecisionManager" ref="voteAccessDecisionManager" />
		<property name="objectDefinitionSource">
			<value>
				CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
				PATTERN_TYPE_APACHE_ANT
				/=ENCUESTAME_ANONYMOUS
				/pages/**=ENCUESTAME_USER
				/pages/admon/**=ENCUESTAME_ADMIN
				/user/**=ENCUESTAME_ANONYMOUS,ENCUESTAME_USER,ENCUESTAME_ADMIN
         </value>
		</property>
	</bean>

FilterChainProxy

El filtro inicializador, su función principal es indicar o personalizar, que recursos ejecutaran los filtros deseados en filterInvocationDefinitionSource, por ejemplo, si tenemos un sevlet /uploadFile y solo nos interesa aplicar algunos filtros

<bean id="springSecurityFilterChain" class="org.springframework.security.util.FilterChainProxy">
      <property name="filterInvocationDefinitionSource">
         <value>
		   CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
		    PATTERN_TYPE_APACHE_ANT
               /**=httpSessionContextIntegrationFilter,logoutFilter,basicProcessingFilter,authenticationProcessingFilter....
              /uploadFile= basicProcessingFilter,OtroFiltroPersonalizado
         </value>
      </property>
    </bean>

En el siguiente artículo, veremos una extensión de los diferentes filtros opcionales que podemos integrar en nuestra seguridad, ¿preguntas?

  • Autor: admin
  • Publicado: May 13th, 2009
  • Categoria: Articulos
  • Comentarios: 1

Spring Security 2, la configuración a la medida con DAO Hibernate II

Tags: , , , , ,

Siguiendo con el articulo anterior, voy a explicar los diferentes Filtros de Seguridad y las Configuraciones Personales que podemos aplicar en nuestra Seguridad. El siguiente gráfico explica el ciclo de vida de la autentificación en Spring.

En el post anterior, vimos el Bean del Formulario de logeo, donde se creo el objeto Authentication que se pasa al AuthenticationManager.

UsernamePasswordAuthenticationToken authReq = new UsernamePasswordAuthenticationToken(userName, password);
.......
Authentication auth = getAuthenticationManager().authenticate(authReq);

spring_exception_filter

AuthenticationManager

El bean AuthenticationManager es del tipo ProviderManager, lo que significa que actúa de proxy con AuthenticationProvider. En Spring, el AuthenticationProvider es el encargado de validar la combinación nombre de usuario/contraseña por medio del objeto Authentication  y retornar los roles asociados a dicho usuario. Esta clase tan sólo delega la autenticación en una lista de proveedores configurable, cada uno de los cuales implementa el interfaz AuthenticationProvider. Hay muchos tipos de AuthenticationProvider( JDBC, Hibernate,  LDAP, RememberMe, OpenID). Usted puede indicar cuales quiere usar son los proveedores de autentificación que desee usar, en nuestro caso, usaremos un DAO Hibernate personalizado.

<bean id="authenticationManager" class="paquete.MiAuthenticationManager">
	<property name="providerString" value="userDaoProvider" />
</bean>
public class MiAuthenticationManager extends ProviderManager {
protected String providerString;
public void setProviderString(String providerString) {
		this.providerString = providerString;
	}
 /**
  * Agrega al Manejador de Proveedores un listado
  */
public void afterPropertiesSet() throws Exception {
		if (providerString != null) {
			List<authenticationprovider> providers = new LinkedList();
			String[] names = providerString.split(",");
			for (String providerUnit : names) {
				AuthenticationProvider provider = (AuthenticationProvider) applicationContext
						.getBean(providerUnit.trim());
				if (provider == null) {
 
					throw new EnMeExpcetion("AuthenticationProvider "
							+ providerUnit + " don't exist");
				}
				providers.add(provider);
			}
			setProviders(providers);
		}
		super.afterPropertiesSet();
	}
}
 
</authenticationprovider>

DaoAuthenticationProvider

DaoAuthenticationProvider, es el comúnmente usado puesto que es el que permite acceder a la información almacenada en una base de datos. El proveedor DaoAuthenticationProvider merece una mención especial. Esta implementación delega a su vez en un objeto de tipo UserDetailsService, un interfaz que define un objeto de acceso a datos con un único método loadUserByUsername que permite obtener la información de un usuario a partir de su nombre de usuario.

<bean id="userDaoProvider"
		class="org.springframework.security.providers.dao.DaoAuthenticationProvider">
		<property name="userDetailsService" ref="dbUserService" />
	</bean>

Si deseas agregarle encriptación a la contraseña del usuario, agregale una propiedad más al userDaoProvider, este artículo que escribi tiempo atrás te ayudará a poner una mejor seguridad que el tipico MD5.

UserDetailsService, un Dao Personalizado

Si no queremos que Spring acceda a la bases de datos directamente podemos configurar un Dao personalizado por medio de una Implementación UserDetailsService, , un interfaz que define un objeto de acceso a datos con un único método loadUserByUsername que permite obtener la información de un usuario a partir de su nombre de usuario.

 
<bean id="dbUserService" class="paquete.MiUserServiceImp">
		<property name="userDao" ref="userDao" />
	</bean>


public class MiUserServiceImp implements UserDetailsService {
         ......
        public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException, DataAccessException {
		SecUsers user = userDao.getUser(username);
		if (user == null) {
			log.info("no encontrado...");
			throw new UsernameNotFoundException("username");
		}
		return convertToUserDetails(user);
	}
 
         ......
 
    protected UserDetails convertToUserDetails(SecUsers user){
         //lista de permisos,
         List<string> listPermissions = new ArrayList</string><string>();
        ...........
        GrantedAuthority[] authorities = new GrantedAuthority[listPermissions
				.size()];
		int i = 0;
		for (String permission : listPermissions) {
			authorities[i++] = new GrantedAuthorityImpl(permission.trim());
		}
 
		User userDetails = new User(user.getUsername(), user.getPassword(),
				user.isStatus() == null ? false : user.isStatus(), true,
				true,
				true,
				authorities);
		log.info("userDetails "+userDetails);
		return userDetails;
    }
}
</string>

Vamos a dejar la explicación de los Filtros, para una tercera parte, ya se hizo largo :) .

Spring Security 2, la configuración a la medida con DAO Hibernate I

Tags: , , , , ,

Uno de los aspectos que toda aplicación debe considerar es la seguridad, entendiendo como tal la necesidad de saber que el usuario es quien dice ser (autenticación), y permitirle acceso sólo a aquellos recursos necesarios (autorización). En un princio el framework  se llamaba Acegi Security e inicio en el 2003, 5 años después en Abril del 2008 se incorporó al portafolio de Spring Framework como un súbmodulo. Spring trae por defecto ciertas caracteristicas que a la hora de querer integrarlas en nuestros sistemas avanzados no encajan a la perfección y por lo general se necesitan modificaciones a las clases principales.

Anteriormente hemos visto configuraciones mucho más sencillas :

Creo que una de las configuraciones más complejas es fusionando con el framework Hibernate y esto nos permite no anclarnos a las tablas que ya Spring Security trae por defecto, asi poder personalizar nuestra seguridad de una mejor manera.

uml

En nuestro caso vamos a trabajar con Spring Framework, y lo ideal es separar nuestra aplicación en 3 capas:

  • Capa de Presentación: En esta capa vamos a programar el formulario, el típico formulario con el nombre de usuario y la contraseña, podemos integrar OPEN ID, e incluso la funcionalidad de Recordar la Sessión por medio de un Cookie.
  • Filtros de Seguridad: Aqui aplicaremos toda la configuración de Spring Security, aplicaremos todos los filtros y las clases que vamos a modificar algunas clases de Spring que se comuniquen con nuestros servicios en la capa de negocio.
  • Servicios en la Capa de Negocio: En esta capa colocaremos nuestros Beans de Spring que a su vez acceden a otra capa en la cual no vamos a entrar en detalle, la de acceso a datos.

Iniciemos con la capa de presentación y el formulario para logearse, en su forma más sencilla, aqui se pueden agregar validadores y mensajes de error, etc.

	<h :inputText id="j_username"  value="#{loginForm.userName}" size="40" required="true"/>
	<h :inputSecret id="j_password" value="#{loginForm.userPassword}" size="40" required="true"/>
	<h :commandButton action="#{loginForm.login}"  value="Iniciar Sesión"/>

Ahora necesitamos nuestro Bean LoginForm, este puedes colocarlo como un ManageBean o un Bean de Spring, como mas te parezca, en mi caso use Spring.

public class LoginForm{
 
        //Bean Personalizado del Manejador de Autentificación
	private AuthenticationManager authenticationManager;
	private String userName;
	private String userPassword;
	private Application app;
 
 
	public String getUserName() {
		return userName;
	}
 
	public void setUserName(String userName) {
		this.userName = userName;
	}
 
	public String getUserPassword() {
		return userPassword;
	}
 
	public void setUserPassword(String userPassword) {
		this.userPassword = userPassword;
	}
 
	public AuthenticationManager getAuthenticationManager() {
		return authenticationManager;
	}
 
	public void setAuthenticationManager(
			AuthenticationManager authenticationManager) {
		this.authenticationManager = authenticationManager;
	}
 
	/**
	 *
 
	/**
	 * Ejecuta un envio a un url definido por la navegación JSF
	 * @param viewId
	 */
	protected void forward(String viewId) {
    	ViewHandler viewHandler = getApplication().getViewHandler();
	    FacesContext facesCtx = getFacesContext();
	    UIViewRoot view = viewHandler.createView(facesCtx, viewId);
	    facesCtx.setViewRoot(view);
	    facesCtx.renderResponse();
	}
 
	/**
	 * Obtiene el contexto de la Aplicación
	 * @return
	 */
	protected Application getApplication() {
		if (app == null) {
			ApplicationFactory appFactory = (ApplicationFactory) FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY);
	        app = appFactory.getApplication();
		}
		return app;
	}
 
	/**
	 * Obtiene el Contexto de JSF
	 * @return
	 */
	protected FacesContext getFacesContext() {
		return FacesContext.getCurrentInstance();
	}
 
	/**
	 * Obtiene la Petición del HttpServletRequest
	 * @return
	 */
	protected HttpServletRequest getRequest() {
		ExternalContext context =
			FacesContext.getCurrentInstance().getExternalContext();
	    HttpServletRequest request =
	    	(HttpServletRequest) context.getRequest();
	    return request;
	}
 
	/**
	 * Obtiene la Respuesta del HttpServletResponse
	 * @return
	 */
	protected HttpServletResponse getResponse() {
		ExternalContext context =
			FacesContext.getCurrentInstance().getExternalContext();
		HttpServletResponse response =
	    	(HttpServletResponse) context.getResponse();
 
	    return response;
	}
 
 
	public String login() {
		HttpServletRequest request = getRequest();
	    try {
 
 
               /**
                 * Obtenemos del Formulario los Datos, por mayor seguridad podriamos agregar filtros contra XSS e Inyección HQL
                 * Creamos un token para Spring
                 */
	    	String userName = getUserName();
	    	String password = getUserPassword();
 
	    	/**
                 * Creamos un token para Spring
                 */
	    	UsernamePasswordAuthenticationToken authReq = new UsernamePasswordAuthenticationToken(userName, password);
 
               /**
                 * Le agregamos al token el request de HttpServletRequest
                 */
	    	authReq.setDetails(new WebAuthenticationDetails(request));
	    	HttpSession session = request.getSession();
 
 
               /**
                 * Asignamos la sesión el atributo UserName
                 * Obtenemos el manager auth y le asignamos el token
                 */
	    	session.setAttribute(AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY, userName);
	    	Authentication auth = getAuthenticationManager().authenticate(authReq);
 
                  /**
                 * Obtenemos el Contexto de Spring Security
                 * Le asignamos el Autentication Manager al Contexto de Seguridad
                 */
 
	    	SecurityContext secCtx = SecurityContextHolder.getContext();
	    	secCtx.setAuthentication(auth);
	    	session.setAttribute(HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY, secCtx);
 
	    	String urlKey = AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY;
	    	SavedRequest savedRequest = (SavedRequest)session.getAttribute(urlKey);
	    	session.removeAttribute(urlKey);
 
	    	String target = "/index.jsf";
	    	/**
                  * Si perdiste la sesión en una página en particular, este parametro te ayudara a regresar a donde perdiste la sesión
                 */
	    	if (savedRequest != null) {
		    	String targetUrl = null;
	    		targetUrl = savedRequest.getFullRequestUrl();
	    		FacesContext.getCurrentInstance().getExternalContext().redirect(targetUrl);
	    		return null;
	    	} else {
	    		.................
	    	}
	    	/**
                 * Si no existiera un SavedRequest, entonces te enviaria a la pagina de inicio
                  */
	    	forward(target);
 
	    } catch (BadCredentialsException e) {
	    	...............
	    	return null;
	    } catch (AuthenticationException e) {
	    	............
	    	return null;
	    } catch (IOException ioException) {
	       ..............
	    }
 
	return "index";
	}
    }
}

Y como lo vamos a integrar con Spring, la declaración del Bean en el contexto

   <bean id="loginForm" class="org.xxx.LoginForm" scope="request">
		<property name="authenticationManager" ref="authenticationManager" />
	</bean>

En la parte II miraremos los Filtros de Seguridad.

© 2009 Jotadeveloper Blog. Nuestros contenidos están bajo licencia Creative Commons mientras no se indique lo contrario,
y pueden reproducirse libremente sin más que mencionar la fuente ("JotaDeveloper") y la URL concreta del artículo original. .

This blog is powered by Wordpress and JotaDeveloperTheme.