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

Veremos cómo realizar una confirmación del registro de usuario "Double Opt-in", 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

Text
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

Comentarios

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

He leído y acepto la política de privacidad

Información básica acerca de la protección de datos

  • Responsable: IZERTIS S.A.
  • Finalidad: Envío información de carácter administrativa, técnica, organizativa y/o comercial sobre los productos y servicios sobre los que se nos consulta.
  • Legitimación: Consentimiento del interesado
  • Destinatarios: Otras empresas del Grupo IZERTIS. Encargados del tratamiento.
  • Derechos: Acceso, rectificación, supresión, cancelación, limitación y portabilidad de los datos.
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad

Software Architect 👷‍♀️ | Tech Leader 🚀 | Senior Developer: Backend 🧩 and occasionally in Frontend 🖼️ | DevSecOps 🛠️ | Banking 🏛️ | Digital Transformation Agent 🎯 | Problem Solver (Mr. Wolf) 🐺

¿Quieres publicar en Adictos al trabajo?

Te puede interesar

30/10/2025

Benjamín Suárez Menéndez

El Complex Problem Solving (CPS) es un proceso estructurado basado en herramientas, técnicas y actitudes que nos facilita la resolución de problemas complejos.

03/10/2025

Miguel García Rodríguez

Descubre cómo el diseño y la psicología del comportamiento utilizan sesgos cognitivos para influir en la toma de decisiones de los usuarios y potenciar la persuasión.

30/09/2025

Iván García Sainz-Aja

En este artículo exploraremos cómo utilizar ZenWave360 para generar un proyecto completo de Spring Boot con Kotlin a partir de un modelo DSL de Lenguaje Ubicuo.