Visión general
Contenido
La primera acción que realiza un cliente después de visitar un sitio web es crear una cuenta, generalmente para realizar un pedido, reservar una cita, pagar un servicio, etc. Al crear una cuenta, es importante conservar la dirección de correo electrónico correcta en el sistema y verificar la propiedad del usuario.
Una estrategia común y eficaz para hacer esto es enviar un enlace de confirmación al registro de la publicación de correo electrónico del usuario. Una vez que el usuario hace clic en el enlace único, su cuenta se activa y puede realizar más acciones en el sitio web.
Spring nos permite implementar esta funcionalidad fácilmente, que es exactamente lo que haremos en este artículo.
Configuración del proyecto
Como siempre, es más fácil comenzar con un Bota de Spring proyecto usando Spring Initializr. Seleccione las dependencias para Web, Security, Mail, JPA, Thymeleaf y MySQL y genere el proyecto:
Estaremos usando Seguridad de Spring y Spring MVC para este proyecto. Para la capa de datos, usaremos Datos de Spring ya que ya nos proporciona operaciones CRUD para una entidad dada y derivación dinámica de consultas a partir de nombres de métodos de repositorio.
Además, usaremos Hibernar como el JPA proveedor y un MySQL base de datos.
Si está interesado en leer más sobre JPA, lo tenemos cubierto aquí: Una guía para Spring Data JPA.
Dependencias
Echemos un vistazo a las dependencias en el pom.xml
archivo, que importa todas las bibliotecas necesarias según la descripción anterior:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
Ahora, con el proyecto configurado, ¡podemos empezar a codificar!
Implementación
Propiedades de Spring
Comencemos configurando las propiedades de Spring en application.properties
:
server.port = 8082
logging.level.org.springframework = WARN
logging.level.org.hibernate = WARN
logging.level.com.springsecurity.demo = DEBUG
####### Data-Source Properties #######
spring.datasource.url = jdbc:mysql://localhost:3306/demodb?useSSL=false
spring.datasource.username = username
spring.datasource.password = password
spring.datasource.driver-class-name = com.mysql.jdbc.Driver
###### JPA Properties ######
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
spring.jpa.generate-ddl = true
spring.jpa.show-sql = true
###### Email Properties ######
spring.mail.host = smtp.gmail.com
spring.mail.port = 587
spring.mail.properties.mail.smtp.starttls.enable = true
spring.mail.username = [email protected]
spring.mail.password = examplepassword
spring.mail.properties.mail.smtp.starttls.required = true
spring.mail.properties.mail.smtp.auth = true
spring.mail.properties.mail.smtp.connectiontimeout = 5000
spring.mail.properties.mail.smtp.timeout = 5000
spring.mail.properties.mail.smtp.writetimeout = 5000
Estamos usando el servidor SMTP de Gmail para este ejemplo. Estoy ejecutando mi servidor tomcat en el puerto 8082
.
Asegúrese de proporcionar las credenciales correctas de MySQL y de la cuenta de correo electrónico según su sistema. Con las propiedades de JPA configuradas, podemos comenzar con nuestra lógica comercial.
Configuración de JPA
Tenemos dos modelos para esta aplicación: User
y ConfirmationToken
:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="user_id")
private long userid;
private String emailId;
private String password;
@Column(name="first_name")
private String firstName;
@Column(name="last_name")
private String lastName;
private boolean isEnabled;
// getters and setters
}
Una clase POJO simple anotada con las anotaciones estándar de Spring.
Ahora pasemos al segundo modelo:
@Entity
public class ConfirmationToken {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="token_id")
private long tokenid;
@Column(name="confirmation_token")
private String confirmationToken;
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
@OneToOne(targetEntity = User.class, fetch = FetchType.EAGER)
@JoinColumn(nullable = false, name = "user_id")
private User user;
public ConfirmationToken(User user) {
this.user = user;
createdDate = new Date();
confirmationToken = UUID.randomUUID().toString();
}
// getters and setters
}
ConfirmationToken
tiene una relación de uno a muchos con el User
entidad. Desde que establecimos jpa.generate-ddl
a true
, Hibernate crea el esquema de tabla según las entidades anteriores.
Las claves primarias en ambas tablas están configuradas para auto-increment
porque hemos anotado las columnas de ID en ambas clases con @Generated(strategy = GenerationType.AUTO)
.
Así es como se ve el esquema generado en la base de datos:
Ahora que la configuración de JPA está hecha, procederemos a escribir la capa de acceso a datos. Para eso usaremos Spring Data, ya que proporciona operaciones CRUD básicas listas para usar, que serán suficientes por el bien de este ejemplo.
Además, al usar Spring Data, libera el código de la placa de caldera, como obtener el administrador de entidades o obtener sesiones, etc.
@Repository("userRepository")
public interface UserRepository extends CrudRepository<User, String> {
User findByEmailIdIgnoreCase(String emailId);
}
Spring Data proporciona automáticamente una implementación para consultar bases de datos sobre la base de un atributo, siempre que sigamos las especificaciones de Java Bean. Por ejemplo, en nuestro POJO, tenemos emailId
como una propiedad de frijol y queremos encontrar el User
por esa propiedad independientemente del caso.
Asimismo, implementamos el repositorio para el ConfirmationToken
también:
public interface ConfirmationTokenRepository extends CrudRepository<ConfirmationToken, String> {
ConfirmationToken findByConfirmationToken(String confirmationToken);
}
Un enfoque común que toman muchos desarrolladores cuando usan Spring Data es usar el repositorio CRUD básico provisto en otra clase de servicio y exponer los métodos de esa clase.
Esto mantiene el código de la aplicación acoplado libremente con las bibliotecas de Spring.
Te puede interesar:Guía de Spring Data JPAServicio de correo electrónico
Una vez que el usuario completa el registro, debemos enviar un correo electrónico a la dirección de correo electrónico del usuario. Usaremos Spring Mail API para lograr esa funcionalidad.
Hemos agregado las propiedades de configuración para esto en el archivo de propiedades que se mostró anteriormente, por lo que podemos continuar con la definición de un servicio de correo electrónico:
@Service("emailSenderService")
public class EmailSenderService {
private JavaMailSender javaMailSender;
@Autowired
public EmailSenderService(JavaMailSender javaMailSender) {
this.javaMailSender = javaMailSender;
}
@Async
public void sendEmail(SimpleMailMessage email) {
javaMailSender.send(email);
}
}
Hemos anotado la clase con @Service
que es una variante del @Component
anotación. Esto permite a Spring Boot descubrir el servicio y registrarlo para su uso.
Controlador y vista
Ahora tenemos todos los servicios listos para nuestro ejemplo y podemos continuar escribiendo el UserAccountController
:
@Controller
public class UserAccountController {
@Autowired
private UserRepository userRepository;
@Autowired
private ConfirmationTokenRepository confirmationTokenRepository;
@Autowired
private EmailSenderService emailSenderService;
@RequestMapping(value="/register", method = RequestMethod.GET)
public ModelAndView displayRegistration(ModelAndView modelAndView, User user)
{
modelAndView.addObject("user", user);
modelAndView.setViewName("register");
return modelAndView;
}
@RequestMapping(value="/register", method = RequestMethod.POST)
public ModelAndView registerUser(ModelAndView modelAndView, User user)
{
User existingUser = userRepository.findByEmailIdIgnoreCase(user.getEmailId());
if(existingUser != null)
{
modelAndView.addObject("message","This email already exists!");
modelAndView.setViewName("error");
}
else
{
userRepository.save(user);
ConfirmationToken confirmationToken = new ConfirmationToken(user);
confirmationTokenRepository.save(confirmationToken);
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setTo(user.getEmailId());
mailMessage.setSubject("Complete Registration!");
mailMessage.setFrom("[email protected]");
mailMessage.setText("To confirm your account, please click here : "
+"http://localhost:8082/confirm-account?token="+confirmationToken.getConfirmationToken());
emailSenderService.sendEmail(mailMessage);
modelAndView.addObject("emailId", user.getEmailId());
modelAndView.setViewName("successfulRegisteration");
}
return modelAndView;
}
@RequestMapping(value="/confirm-account", method= {RequestMethod.GET, RequestMethod.POST})
public ModelAndView confirmUserAccount(ModelAndView modelAndView, @RequestParam("token")String confirmationToken)
{
ConfirmationToken token = confirmationTokenRepository.findByConfirmationToken(confirmationToken);
if(token != null)
{
User user = userRepository.findByEmailIdIgnoreCase(token.getUser().getEmailId());
user.setEnabled(true);
userRepository.save(user);
modelAndView.setViewName("accountVerified");
}
else
{
modelAndView.addObject("message","The link is invalid or broken!");
modelAndView.setViewName("error");
}
return modelAndView;
}
// getters and setters
}
Echemos un vistazo a los métodos en el controlador, qué hacen y qué vistas devuelven.
displayRegistration()
– Punto de partida para el usuario en nuestra aplicación. Tan pronto como el usuario abre nuestra aplicación, se le muestra la página de registro a través de este método.
También hemos agregado el user
Objeto a la vista. los <form>
La etiqueta en la página de registro también incluye este objeto y usaremos los campos en el formulario para completar los campos del objeto.
Este objeto, con la información completa, se conservará en la base de datos.
Te puede interesar:Cómo acceder a la API de Facebook con Java y Spring BootAsí es como se ve en la página:
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Register</title>
</head>
<body>
<form action="#" th:action="@{/register}" th:object="${user}" method="post">
<table>
<tr>
<td><label for="firstName">First Name</label></td>
<td><input th:field="*{firstName}" type="text" name="firstName"></input></td>
</tr>
<tr>
<td><label for="lastName">Last Name</label></td>
<td><input th:field="*{lastName}" type="text" name="lastName"></input></td>
</tr>
<tr>
<td><label for="emailId">Email</label></td>
<td><input th:field="*{emailId}" type="text" name="emailId"></input></td>
</tr>
<tr>
<td><label for="password">Password</label></td>
<td><input th:field="*{password}" type="password" name="password"></input></td>
</tr>
<tr>
<td><input type="reset" value="Clear"/></td>
<td><input type="submit" value="Submit"></input></td>
</tr>
</table>
</form>
</body>
</html>
registerUser()
– Acepta los datos de usuario introducidos en la página de registro. Spring MVC automáticamente hace que la entrada del usuario esté disponible para nosotros en el método.
Guardamos los detalles del usuario en la tabla de usuarios y creamos un token de confirmación aleatorio. El token se guarda con el usuario emailId
en el confirmation_token
tabla y se envía a través de una URL al correo electrónico del usuario para su verificación.
Se muestra al usuario una página de registro exitosa:
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Registration Success</title>
</head>
<body>
<center>
<span th:text="'A verification email has been sent to: ' + ${emailId}"></span>
</center>
</body>
</html>
Por último, una vez que se accede a la URL del correo electrónico, confirmUserAccount()
se llama al método.
Este método valida el token que no debe estar vacío y debe existir en la base de datos; de lo contrario, se muestra al usuario una página de error (error.html)
:
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Registration Success</title>
</head>
<body>
<center>
<span th:text="${message}"></span>
</center>
</body>
</html>
Si no hay problemas de validación, se verifica la cuenta asociada con el token. Se muestra al usuario un mensaje de activación exitosa:
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Congratulations!</title>
</head>
<body>
<center>
<h2>Congratulations! Your account has been activated and email is verified!</h2>
</center>
</body>
</html>
Configuración de seguridad de Spring
Configuremos ahora el módulo Spring Security para asegurar nuestra aplicación web. Necesitamos asegurarnos de que no se requiera autenticación para /register
y /confirm
URL, ya que son las páginas de destino de un nuevo usuario:
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/register").permitAll()
.antMatchers("/confirm").permitAll();
}
}
Por último, necesitaremos un main
método como punto de partida para nuestra aplicación Spring Boot:
@SpringBootApplication
public class RunApplication {
public static void main(String[] args) {
SpringApplication.run(RunApplication.class, args);
}
}
los @SpringBootApplication
La anotación indica a Spring Boot que cargue toda la configuración y las clases de componentes y también habilita la configuración automática. Esta es una de las grandes características de Spring Boot, podemos ejecutarlo usando un método principal simple.
Ejecutando la Aplicación
Comenzamos la prueba seleccionando y ejecutando el RunApplication.java
. Esto inicia el servidor Tomcat integrado en el puerto 8082
y nuestra aplicación se implementa.
A continuación, abramos el navegador y accedamos a nuestra aplicación:
Al ingresar la información requerida, el usuario enviará:
Tras el envío, se envía un correo electrónico al usuario para verificar el correo electrónico:
Siguiendo el enlace, la cuenta se verifica mediante un token único y el usuario es redirigido a la página de éxito:
Así es como se ve en la base de datos:
Te puede interesar:Introducción a Apache Spark con JavaConclusión
La verificación de correo electrónico es un requisito muy común para las aplicaciones web. Casi cualquier tipo de registro requiere una cuenta de correo electrónico verificada, especialmente si el usuario ingresa algún tipo de información sensible en el sistema.
En este artículo, escribimos una aplicación Spring Boot simple para generar tokens únicos para nuevos usuarios, enviarles un correo electrónico y verificarlos en nuestro sistema.
Si está interesado en jugar con el código fuente, como siempre, está disponible en GitHub