Send an activation link to email for the new user registration using Spring Boot Application

By | September 4, 2020

In this article, we will learn to send the activation link to the user’s email address once they registered on our spring boot application.

In most of the websites, when you registered or create a new account, then you automatically received an activation link to your email address, and once you click on that link, your account gets fully activated.

So, this feature we are going to implement in our spring boot application and will build the application from scratch to finish.

Final Flow Diagram of Application

Send an activation link to email for the new user registration using Spring Boot Application

Note: Video Tutorial is available in bottom section of this tutorial.

Please follow the step by step as I mentioned below.

Step 1: Create a Project from Spring Initializr.

  • Go to the Spring Initializr.
  • Enter a Group name, com.pixeltrice.
  • Mention the Artifact Id, spring-boot-send-activation-link
  • Add the following dependencies,
    1. Spring Web.
    2. Spring Data JPA.
    3. Spring Security.
    4. MySQL Driver.
    5. Thymeleaf.
    6. Java Mail Sender.

Step 2: Click on the Generate button, the project will be downloaded on your local system.

Step 3: Unzip and extract the project.

Step 4: Import the project in your IDE such as Eclipse.

Select File -> Import -> Existing Maven Projects -> Browse -> Select the folder spring-boot-send-activation-link-> Finish.

Step 5: Configure the application.properties file.

In this step we will configure some properties in the application.properties file.

####### MySQL Data-Source Properties #######
spring.datasource.url = jdbc:mysql://localhost:3306/activation-link?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 ######
#smtp mail properties
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=your email address through which you will send the link
spring.mail.password= password of your email address
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true

Explanation of application.properties file.

If you noticed, the above file contains three types of properties.

  1. Data Source Properties.
  2. JPA Properties.
  3. Email Properties.

Data Source Properties

We are going to use the MySQL as a data source in this application for storing the data of the users.

  • spring.datasource.url: It contains the URL to connect with the database or schema in the MySQL Workbench.
  • spring.datasource.username: Username to access the database.
  • spring.datasource.password: Password of the database.
  • spring.datasource.driver-class-name: Driver name.

JPA Properties

Since we are using a Hibernate as JPA, to perform all SQL or database operation, so all the important properties we have configured in this section.

  • spring.jpa.generate-ddl = true, Since we have set the value true, then Hibernate will automatically create a table in the database based on the Entity class.
  • spring.jpa.show-sql = true, You can able to see all the SQL Queries performed by the Hibernate.

Email Properties

As you know we are developing an application to send the activation link to the user’s email address, so in the application.properties file we need to provide the email address.

So on behalf of that email address, the Spring Boot Application will send the confirmation or activation link to the registered user’s email. And to achieve this we are using Gmail.

Note: Make sure that MySQL username and password are correct.

Step 6: Create an Entity class or JPA Configuration Class.

In this step, we have to create an Entity class that represents the table or mapped with the database table. So we need to create two entity classes, one is for storing the user detail and another for the Confirmation link.

  1. UserEntity.java
  2. ConfirmationToken.java

UserEntity.java

package com.pixeltrice.springbootsendactivationlink;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "users")
public class UserEntity {

    @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 methods
}

As you can see we have created an Entity class, which will be mapped with table named users in the database.

ConfirmationToken.java

package com.pixeltrice.springbootsendactivationlink;

import java.util.Date;
import java.util.UUID;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name="confirmationToken")
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 = UserEntity.class, fetch = FetchType.EAGER)
    @JoinColumn(nullable = false, name = "user_id")
    private UserEntity userEntity;

    public ConfirmationToken() {}

    public ConfirmationToken(UserEntity userEntity) {
        this.userEntity = userEntity;
        createdDate = new Date();
        confirmationToken = UUID.randomUUID().toString();
    }

    // getters and setters
}

Here the table confirmationToken has one to one relationships with the users’ table, and in both the entity classes we declared @Id annotation which represents the primary key.

Step 7: Create a Repository Interface for UserEntity and Confirmation Token

In this step, we will add the Data Access Layer which provided by the Spring Data. It provides all the basic CRUD Operation need to be performed on database level.

UserRepository.java

package com.pixeltrice.springbootsendactivationlink;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository("userRepository")
public interface UserRepository extends CrudRepository<UserEntity, String> {

    UserEntity findByEmailIdIgnoreCase(String emailId);
}

ConfirmationTokenRepository.java

package com.pixeltrice.springbootsendactivationlink;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository("confirmationTokenRepository")
public interface ConfirmationTokenRepository extends CrudRepository<ConfirmationToken, String> {
    ConfirmationToken findByConfirmationToken(String confirmationToken);
}

Step 8: Create the Email Service class

This service class is required to send the activation or confirmation link to the user’s email address once they completed the registration. To achieve this functionality we are using the Spring Mail API.

As you know we have already added the email related properties in the application.properties file, so Email Service class we need to define the method to send the email.

EmailService.java

package com.pixeltrice.springbootsendactivationlink;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service("emailService")
public class EmailService {

    private JavaMailSender javaMailSender;

    @Autowired
    public EmailService(JavaMailSender javaMailSender) {
        this.javaMailSender = javaMailSender;
    }

    @Async
    public void sendEmail(SimpleMailMessage email) {
        javaMailSender.send(email);
    }
}

Step 9: Create a UserAccountController class

In this step, we will define all the necessary API such as for new user registration, API for confirming the user account, etc.

UserAccountController.java

package com.pixeltrice.springbootsendactivationlink;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class UserAccountController {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ConfirmationTokenRepository confirmationTokenRepository;

    @Autowired
    private EmailService emailService;

    @RequestMapping(value="/register", method = RequestMethod.GET)
    public ModelAndView displayRegistration(ModelAndView modelAndView, UserEntity userEntity)
    {
        modelAndView.addObject("userEntity", userEntity);
        modelAndView.setViewName("register");
        return modelAndView;
    }
    
    
    
    @RequestMapping(value="/register", method = RequestMethod.POST)
    public ModelAndView registerUser(ModelAndView modelAndView, UserEntity userEntity)
    {

    	UserEntity existingUser = userRepository.findByEmailIdIgnoreCase(userEntity.getEmailId());
        if(existingUser != null)
        {
            modelAndView.addObject("message","This email already exists!");
            modelAndView.setViewName("error");
        }
        else
        {
            userRepository.save(userEntity);

            ConfirmationToken confirmationToken = new ConfirmationToken(userEntity);

            confirmationTokenRepository.save(confirmationToken);

            SimpleMailMessage mailMessage = new SimpleMailMessage();
            mailMessage.setTo(userEntity.getEmailId());
            mailMessage.setSubject("Complete Registration!");
            mailMessage.setFrom("YOUR EMAIL ADDRESS");
            mailMessage.setText("To confirm your account, please click here : "
            +"http://localhost:8080/confirm-account?token="+confirmationToken.getConfirmationToken());

            emailService.sendEmail(mailMessage);

            modelAndView.addObject("emailId", userEntity.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)
        {
        	UserEntity user = userRepository.findByEmailIdIgnoreCase(token.getUserEntity().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;
    }
}

Explanation of each line of code.

1.displayRegistration()

 @RequestMapping(value="/register", method = RequestMethod.GET)
    public ModelAndView displayRegistration(ModelAndView modelAndView, UserEntity userEntity)
    {
        modelAndView.addObject("userEntity", userEntity);
        modelAndView.setViewName("register");
        return modelAndView;
    }

Once the user browses or visits our application, then this method will display the registration form to them.

HTML Page which display the registration form to new user.

<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="${userEntity}" 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>

2. registerUser()

Once the user fills the registration form and clicks on the submit button then this method gets triggered or executed.

 @RequestMapping(value="/register", method = RequestMethod.POST)
    public ModelAndView registerUser(ModelAndView modelAndView, UserEntity userEntity)
    {

    	UserEntity existingUser = userRepository.findByEmailIdIgnoreCase(userEntity.getEmailId());
        if(existingUser != null)
        {
            modelAndView.addObject("message","This email already exists!");
            modelAndView.setViewName("error");
        }
        else
        {
            userRepository.save(userEntity);

            ConfirmationToken confirmationToken = new ConfirmationToken(userEntity);

            confirmationTokenRepository.save(confirmationToken);

            SimpleMailMessage mailMessage = new SimpleMailMessage();
            mailMessage.setTo(userEntity.getEmailId());
            mailMessage.setSubject("Complete Registration!");
            mailMessage.setFrom("YOUR EMAIL ADDRESS");
            mailMessage.setText("To confirm your account, please click here : "
            +"http://localhost:8080/confirm-account?token="+confirmationToken.getConfirmationToken());

            emailService.sendEmail(mailMessage);

            modelAndView.addObject("emailId", userEntity.getEmailId());

            modelAndView.setViewName("successfulRegisteration");
        }

        return modelAndView;
    }
    

First, it will verify and compare that user’s email address with the email addresses exists in the database, if it finds, then it will give redirect to the error page along with the message, “This email already exists!”.

View or html page for error that is error.html will look like as shown below.

<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>

Otherwise, if there is no validation issue or the email address is completely new then the user’s detail will be saved in the users’ table and then the random confirmation token will be created and stored in the confirmation_token table along with the email address.

And once all the above things are completed then that confirmation token will be sent to the user’s email address in the form of an activation or confirmation link.

The user will get the view of successfulRegisteration.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="'A verification email has been sent to: ' + ${emailId}"></span>
        </center>
    </body>
</html>

3. Confirmation of User Account.

  @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)
        {
        	UserEntity user = userRepository.findByEmailIdIgnoreCase(token.getUserEntity().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;
    }

In this API, once the user clicks on the link which they received on their email, then confirmUserAccount() method will be executed, and in which it will validate the token is not empty and it should be present in the database, otherwise user will redirect to the error.html along with the message, “The link is invalid or broken”.

<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>

Else if the token is successfully validated then the user will redirect to the accountVerified view that is accountVerified.html

<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
  xmlns:th="http://www.thymeleaf.org">
    <head>
        <title>Congratulations!</title>
    </head>
    <body>
        <center>
            <h3>Congratulations! Your account has been activated and email is verified!</h3>
        </center>
    </body>
</html>

Note: Make sure all the HTML files should be placed under a templates folder in the following path \src\main\resources\templates.

Step 10: Configure the Spring Security

In this step, we will implement and secure our web application using Spring Security.

SecurityConfiguration.java

package com.pixeltrice.springbootsendactivationlink;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                .antMatchers("/register").permitAll()
                .antMatchers("/confirm").permitAll();
        }
}

Explanation

To enable the Spring Security we need to add the annotation @EnableWebSecurity and at the same time need to extends the WebSecurityConfigurerAdapter class to override the configure method.

configure() method Explanation

protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                .antMatchers("/register").permitAll()
                .antMatchers("/confirm").permitAll();
        }

In this method, we defined any HTTP Request comes our application needs to be authenticated except for /register and /confirm URLs, since both required to register or create a new user account.

Step 11: Spring Boot main class

package com.pixeltrice.springbootsendactivationlink;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootSendActivationLinkApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootSendActivationLinkApplication.class, args);
	}

}

This is the root of the driving class of our Spring boot application which directs Spring Boot to loads all the components as well as configuration classes and also enables the auto-configuration.

Note: Before running the application make sure the pom.xml file must have all necessary dependencies.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.pixeltrice</groupId>
	<artifactId>spring-boot-send-activation-link</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-boot-send-activation-link</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>11</java.version>
	</properties>

	<dependencies>
		<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-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</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-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

And also verify once that MySQL properties such as username and password are correct in the application.properties file.

Final Folder Structure.

Send an email with an activation link of the new user account with Spring Boot Application

Note: Make sure all the HTML files must be present and it should be in the right path as shown in the above figure.

Step 12: Run the application.

Once the application has started Running, the table has been created with named users and confirmation_token as you can see in the figure.

Send an activation link to email for the new user registration using Spring Boot Application

Please go to the URL localhost:8080/register. You will see the following form.

Send an activation link to email for the new user registration using Spring Boot Application

Click on the Submit button after filling all the necessary detail, you will get the following message on your screen.”A verification email has been sent to example@gmail.com”.

Send an activation link to email for the new user registration using Spring Boot Application

Note: If you are getting any error like javax.mail.AuthenticationFailedException: 535-5.7.8 Username and Password not accepted. Then might happen either of any mentioned reason below.

  1. Less Secure app is not enabled on your Gmail account(Turn it On)(Email Address provided in application.properties file).
  2. Two-step verification might be turned on. (please do off it for testing).

Go to the email address which you provided at the time of registration. You will see the mail something like as shown below.

Send an activation link to email for the new user registration using Spring Boot Application

Once you press on the link, You will redirect to the screen where the account activation message will be displayed.

Send an activation link to email for the new user registration using Spring Boot Application

Download the Source Code.

Summary

In this article, we have learned and build to spring boot application from scratch to send the confirmation or activation link to their email address for newly registered users. If you have any doubt or query please feel free to ask me anytime. I am always available to solve your queries.

You might also like this article.

Leave a Reply

Your email address will not be published. Required fields are marked *