Categorías del Tutorial

icono_twiter
Jose Manuel Sánchez Suárez

Consultor tecnológico de desarrollo de proyectos informáticos.

Puedes encontrarme en Autentia: Ofrecemos servicios de soporte a desarrollo, factoría y formación

Somos expertos en Java/J2EE

Ver todos los tutoriales del autor

Fecha de publicación del tutorial: 2012-08-24

Tutorial visitado 3.059 veces Descargar en PDF
Double Opt-In y autologin con el soporte de Spring MVC y Spring Security.

Double Opt-In y autologin con el soporte de Spring MVC y Spring Security.


0. Índice de contenidos.


1. Introducción

"Double Opt-In" es un término anglosajón que hace referencia al doble consentimiento que se solicita a un usuario para ser incluido en una lista de correo electrónico o a un potencial cliente para que confirme esa dirección de email. El objetivo en ambos casos es confirmar que la dirección proporcionada se corresponde con una dirección real y, además, pertenece a la persona que se registra en nuestro site.

La secuencia temporal sería la siguiente:

  • un usuario proporciona sus datos personales y dirección de correo electrónico en un formulario de nuestro site,
  • nuestra lógica de negocio valida la unicidad de la dirección de correo, crea el usuario inactivo y envía un correo electrónico con un enlace de confirmación para que el usuario acceda al mismo, hasta que no acceda el usuario seguirá inactivo; el enlace incluye un token cifrado que identifica de forma unívoca el registro del usuario,
  • el usuario recibe el email de confirmación, pulsa sobre el enlace y nuestra lógica de negocio valida que el token se corresponde con un usuario inactivo, lo activa y, para no volver a solicitarle usuario y contraseña, le autologa.

El ámbito de este tutorial cubre el último punto de la secuencia y vamos a analizar cómo llevarlo a cabo con el soporte de Spring MVC y Spring Security. El resto:

  • el registro de usuario no tiene mayor complejidad que la recepción y validación de la información de un formulario, que podemos hacerlo también con el soporte de Spring MVC,
  • al crear el usuario, se marcará como inactivo y podemos almacenar en la información del mismo el token del registro, una clave cifrada generada a partir de su email, por ejemplo, con un salt, y
  • para el envío del correo electrónico podemos seguir este tutorial, para implementarlo también con el soporte de Spring.

2. Entorno.

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15' (2.4 GHz Intel Core i7, 8GB DDR3 SDRAM).
  • Sistema Operativo: Mac OS X Lion 10.7.4
  • Spring MVC 3.1.2.RELEASE
  • Spring Security 3.1.2.RELEASE

3. Configuración.

Para lo que nos ocupa, la configuración de nuestro proyecto no tiene relevancia; importaremos con el soporte de maven las dependencias de spring que necesitemos, a priori:

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
            <version>3.1.2.RELEASE</version>
		</dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>3.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>3.1.2.RELEASE</version>
        </dependency>

Y, a nivel de Spring Security, no tenemos más que seguir los pasos habituales para la autenticación tradicional, por ejemplo siguiendo este tutorial, puesto que no afecta al Double Opt-in, no vamos a hacer uso del authenticationManager.


4. Lógica de control.

El usuario recibiría un correo electrónico con un enlace del tipo http://www.mi-sitio.com/registro/confirmacion-mail?t=adsg78huiomkl34 que, con el soporte de Spring MVC, podríamos mapear de la siguiente forma:


@Controller
public class RegisterCtrl implements Serializable{
	
	@Resource
	private RegistrationCustomer registrationCustomer;
	
	@RequestMapping(method=RequestMethod.GET, value="registro/confirmacion-mail")
	public ModelAndView registrationCustomerConfirmation( @RequestParam(value="t") String token) {
		registrationCustomer.confirm(token);
		final ModelAndView modelAndView = new ModelAndView("mailRegistrationCustomerConfirmationView");
		return modelAndView;
	}
	
}

La lógica de control únicamente delega en la lógica de negocio, que veremos en el siguiente punto. La gestión de errores es genérica, si el token de registro no existe o el usuario ya está activo, devolvemos un 404, mapeando una excepción propia; veremos como más adelante.

Solo quedaría crear la vista mailRegistrationCustomerConfirmationView con el soporte de Spring MVC para informar al usuario sobre la confirmación correcta de su registro.


5. Lógica de negocio.

La lógica de negocio la implementa un servicio de Spring que podría tener un código similar al siguiente:

@Service
public class RegistrationCustomer implements Serializable{
	
	@Resource
	private CustomerRepository customerRepository;
	
	public void confirm(String token) {
		final Customer customer = customerRepository.getInactiveByToken(token);
		if (customer == null){
			throw new ResourceNotFoundException("Inactive customer not found by hash code");
		}
		customer.setActive(true);
		SecurityUtils.autoLogin(customer);
	}		

La clase Customer es un objeto de negocio propio que mapea las propiedades de la tabla de usuario.

No vamos a mostrar el contenido de la clase de repositorio (CustomerRepository) que realiza la consulta del usuario inactivo por token, asumimos que tenemos el soporte de Hibernate u otro framework de persitencia que realizaría una consulta a la tabla de usuarios en busca de uno con dicho token y marcado como inactivo.

La clase de utilidades que realmente realiza el autologin tendría un código similar al siguiente:

public final class SecurityUtils {
	
	private static final String ROLE_CUSTOMER = "ROLE_CUSTOMER";
		
	private SecurityUtils(){}

	public static void autoLogin(Customer customer) {
		final Authentication authentication = getUserCredentials(customer);
		getSecurityContextHolder().setAuthentication(authentication);
	}
	
	private static Authentication getUserCredentials(Customer user) {
		final Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
		authorities.add(new SimpleGrantedAuthority(ROLE_CUSTOMER));
		return new RememberMeAuthenticationToken(user.getEmail(), user.getEmail(), authorities); 
	}
	
	static SecurityContext getSecurityContextHolder() {
		return SecurityContextHolder.getContext();
	}
	
}

Como en este punto, no se ha realizado un login al uso, esto es, no ha habido un formulario de petición de usuario y contraseña, no podemos utilizar un UsernamePasswordAuthenticationToken, es por ello, que usamos un RememberMeAuthenticationToken.

Y, como nuestros clientes tienen un único rol de usuario, asignamos a todos una única credencial "ROLE_CUSTOMER", si tuvieramos más esa informacion estaría mapeada en la propia clase Customer y la obtendríamos de ella.


6. Gestión de excepciones.

Nuestra excepción tendría un código similar al siguiente:

@ResponseStatus(value=HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
	
	private final String resource;
	private Throwable cause;
	
	public ResourceNotFoundException(String resource){
		this.resource = resource;
	}
	
	public ResourceNotFoundException(String resource,
			Throwable cause) {
		this.resource = resource;
		this.cause = cause;
	}
}

Con la anotación @ResponseStatus se estamos diciendo a Spring MVC que cuando se lance esta excepción se envíe al usuario un 404.


7. Conclusiones.

También hay que decir, que la técnica del "Double Opt-in" se convierte en "Single Opt-in" cuando estamos en un proceso de compra o, lo que es lo mismo, en la confirmación del contenido de un carrito. En ese caso, si el usuario no está registrado, al realizar el registro no se solicita confirmación del correo para no perder una compra ;)

Un saludo.

Jose

jmsanchez@autentia.com

A continuación puedes evaluarlo:

Regístrate para evaluarlo

Por favor, vota +1 o compártelo si te pareció interesante

Share |
Anímate y coméntanos lo que pienses sobre este TUTORIAL: