Implementation to Activate a New Account by Email in Spring Security

Below is the implementation to activate a new account by email in spring security.

Step 1: Create a new Spring Boot project using Spring Initializr and include the required dependencies,

  • Spring Web
  • Spring Security
  • Java Mail Sender
  • Spring Data JPA
  • MySQL Driver
  • Validation
  • Thymeleaf
  • Lombok
  • Spring DevTools

After the project creation done, the folder structure will be like below.


Step 2: Open application.properties file and add the configuration for the MySQL database, mail and thymeleaf of the Spring Security application in the project.

# Database connection settings
spring.datasource.url=jdbc:mysql://localhost:3306/email_verification
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# Hibernate settings
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update

# Mail settings (replace with your mail server details)
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=mahi.looser12@gmail.com
spring.mail.password=rsglsiokhazsl
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true


Create the message.properties file and add the subject and message of the email service of the application.

email.subject=Email Verification
email.message=Click the link to verify your email:


Step 3: Create a new package named model and in that package, create a new Java class named User.

Go to src > com.gfg.springsecurityaccountactivateemail > model > User and put the below code.

Java
package org.example.springsecurityaccountactivateemail.model;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.UUID;

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private UUID id;

    @Column(nullable = false, unique = true)
    private String email;

    @Column(nullable = false)
    private String password;

    @Column(nullable = false)
    private boolean enabled;

    private String verificationToken;

}


Step 4: Create a new package named repository. In that package, create a new Java interface named UserRepository.

Go to src > com.gfg.springsecurityaccountactivateemail > repository > UserRepository and put the below code.

Java
package org.example.springsecurityaccountactivateemail.repository;


import org.example.springsecurityaccountactivateemail.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;
import java.util.UUID;

public interface UserRepository extends JpaRepository<User, UUID> {
    Optional<User> findByEmail(String email);
    Optional<User> findByVerificationToken(String token);
}


Step 5: Create a new package named dto. In that package, create a new Java class named RegistrationDto.

Go to src > com.gfg.springsecurityaccountactivateemail > dto > RegistrationDto and put the below code.

Java
package org.example.springsecurityaccountactivateemail.dtos;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserRegistrationDto {
    private String email;
    private String password;

}


Step 6: Create a new package named service. In that package, create a new Java class named UserService.

Go to src > com.gfg.springsecurityaccountactivateemail > service > UserService and put the below code.

Java
package org.example.springsecurityaccountactivateemail.service;


import org.example.springsecurityaccountactivateemail.dtos.UserRegistrationDto;
import org.example.springsecurityaccountactivateemail.model.User;
import org.example.springsecurityaccountactivateemail.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.UUID;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private EmailService emailService;

    public User registerNewUserAccount(UserRegistrationDto userDto) {
        User user = new User();
        user.setEmail(userDto.getEmail());
        user.setPassword(new BCryptPasswordEncoder().encode(userDto.getPassword()));
        user.setEnabled(false);
        userRepository.save(user);

        // Generate verification token and send email
        String token = UUID.randomUUID().toString();
        user.setVerificationToken(token);
        userRepository.save(user);

        String confirmationUrl = "http://localhost:8080/verify-email?token=" + token;
        emailService.sendEmail(user.getEmail(), "Email Verification", "Click the link to verify your email: " + confirmationUrl);

        return user;
    }

    public String validateVerificationToken(String token) {
        User user = userRepository.findByVerificationToken(token).orElse(null);
        if (user == null) {
            return "invalid";
        }

        user.setEnabled(true);
        userRepository.save(user);
        return "valid";
    }
}


Step 7: Create a new package named service. In that package, create a new Java class named EmailService.

Go to src > com.gfg.springsecurityaccountactivateemail > service > EmailService and put the below code.

Java
package org.example.springsecurityaccountactivateemail.service;

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

@Service
public class EmailService {

    @Autowired
    private JavaMailSender mailSender;

    public void sendEmail(String to, String subject, String text) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo(to);
        message.setSubject(subject);
        message.setText(text);
        mailSender.send(message);
    }
}


Step 8: Create a new package named service. In that package, create a new Java class named VerificationService.

Go to src > com.gfg.springsecurityaccountactivateemail > service > VerificationService and put the below code.

Java
package org.example.springsecurityaccountactivateemail.service;

import org.example.springsecurityaccountactivateemail.model.User;
import org.example.springsecurityaccountactivateemail.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class VerificationTokenService {

    @Autowired
    private UserRepository userRepository;

    public void createVerificationToken(User user, String token) {
        user.setVerificationToken(token);
        userRepository.save(user);
    }

    public String validateVerificationToken(String token) {
        User user = userRepository.findByVerificationToken(token).orElse(null);
        if (user == null) {
            return "invalid";
        }

        user.setEnabled(true);
        userRepository.save(user);
        return "valid";
    }
}


Step 8: Create a new package named config. In that package, create a new Java class named SecurityConfig.

Go to src > com.gfg.springsecurityaccountactivateemail > config > SecurityConfig and put the below code.

Java
package org.example.springsecurityaccountactivateemail.config;

import org.springframework.context.annotation.Bean;
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.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests((requests) -> requests
                        .requestMatchers("/register", "/verify-email", "/login").permitAll()
                        .anyRequest().authenticated()
                )
                .formLogin((form) -> form
                        .loginPage("/login")
                        .permitAll()
                )
                .logout((logout) -> logout.permitAll());

        return http.build();
    }
}


Step 9: Create a new package named util. In that package, create a new Java class named RandomStringGenerator.

Go to src > com.gfg.springsecurityaccountactivateemail > util > RandomStringGenerator and put the below code.

Java
package org.example.springsecurityaccountactivateemail.util;

import java.security.SecureRandom;

public class RandomStringGenerator {

    private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    private static final SecureRandom random = new SecureRandom();

    public static String generateString(int length) {
        StringBuilder sb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            sb.append(CHARACTERS.charAt(random.nextInt(CHARACTERS.length())));
        }
        return sb.toString();
    }
}


Step 10: Create a new package named event. In that package, create a new Java class named OnRegistrationCompleteEvent.

Go to src > com.gfg.springsecurityaccountactivateemail > event > OnRegistrationCompleteEvent and put the below code.

Java
package org.example.springsecurityaccountactivateemail.event;

import org.example.springsecurityaccountactivateemail.dtos.UserRegistrationDto;
import org.example.springsecurityaccountactivateemail.model.User;
import org.example.springsecurityaccountactivateemail.repository.UserRepository;
import org.example.springsecurityaccountactivateemail.service.EmailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.UUID;

public class OnRegistrationCompleteEvent extends ApplicationEvent {
    private final User user;

    public OnRegistrationCompleteEvent(User user) {
        super(user);
        this.user = user;
    }

    public User getUser() {
        return user;
    }
}


Step 11: In event package, create a new Java class named RegistrationListener.

Go to src > com.gfg.springsecurityaccountactivateemail > event > RegistrationListener and put the below code.

Java
package org.example.springsecurityaccountactivateemail.event;

import org.example.springsecurityaccountactivateemail.model.User;
import org.example.springsecurityaccountactivateemail.service.EmailService;
import org.example.springsecurityaccountactivateemail.service.VerificationTokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.util.UUID;

@Component
public class RegistrationListener implements ApplicationListener<OnRegistrationCompleteEvent> {

    @Autowired
    private VerificationTokenService tokenService;

    @Autowired
    private EmailService emailService;

    @Override
    public void onApplicationEvent(OnRegistrationCompleteEvent event) {
        this.confirmRegistration(event);
    }

    private void confirmRegistration(OnRegistrationCompleteEvent event) {
        User user = event.getUser();
        String token = UUID.randomUUID().toString();
        tokenService.createVerificationToken(user, token);

        String recipientAddress = user.getEmail();
        String subject = "Email Verification";
        String confirmationUrl = "http://localhost:8080/verify-email?token=" + token;
        String message = "Click the link to verify your email: " + confirmationUrl;

        emailService.sendEmail(recipientAddress, subject, message);
    }
}


Step 12: Create a new package named controller. In that package, create a new Java class named RegistrationController.

Go to src > com.gfg.springsecurityaccountactivateemail > controller > RegistrationController and put the below code.

Java
package org.example.springsecurityaccountactivateemail.controller;

import org.example.springsecurityaccountactivateemail.dtos.UserRegistrationDto;
import org.example.springsecurityaccountactivateemail.event.OnRegistrationCompleteEvent;
import org.example.springsecurityaccountactivateemail.model.User;
import org.example.springsecurityaccountactivateemail.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class RegistrationController {

    @Autowired
    private UserService userService;

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    @GetMapping("/register")
    public String showRegistrationForm(Model model) {
        model.addAttribute("user", new UserRegistrationDto());
        return "registration";
    }

    @PostMapping("/register")
    public String registerUserAccount(@ModelAttribute("user") UserRegistrationDto userDto, Model model) {
        User registered = userService.registerNewUserAccount(userDto);
        eventPublisher.publishEvent(new OnRegistrationCompleteEvent(registered));
        return "redirect:/verify-email";
    }

    @GetMapping("/verify-email")
    public String verifyEmail(@RequestParam("token") String token, Model model) {
        String result = userService.validateVerificationToken(token);
        if (result.equals("valid")) {
            model.addAttribute("message", "Your account has been verified successfully.");
            return "verified";
        } else {
            model.addAttribute("message", "Invalid verification token.");
            return "verify-email";
        }
    }
}


Step 13: In controller package, create a new Java class named LoginController.

Go to src > com.gfg.springsecurityaccountactivateemail > controller > LoginController and put the below code.

Java
package org.example.springsecurityaccountactivateemail.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class LoginController {

    @GetMapping("/login")
    public String showLoginForm() {
        return "login";
    }
}


Step 14: Open the main class.

Java
package org.example.springsecurityaccountactivateemail;

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

@SpringBootApplication
public class SpringSecurityAccountActivateEmailApplication {

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

}


Step 15: Create the Registration HTML file.

Go to src > main > resources > templates > registration.html and put the below HTML code.

HTML
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Registration</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<div class="container">
    <h2 class="mt-5">Register</h2>
    <form th:action="@{/register}" method="post" class="needs-validation" novalidate>
        <div class="mb-3">
            <label for="email" class="form-label">Email</label>
            <input type="email" class="form-control" id="email" name="email" required/>
            <div class="invalid-feedback">
                Please provide a valid email.
            </div>
        </div>
        <div class="mb-3">
            <label for="password" class="form-label">Password</label>
            <input type="password" class="form-control" id="password" name="password" required/>
            <div class="invalid-feedback">
                Please provide a password.
            </div>
        </div>
        <button type="submit" class="btn btn-primary">Register</button>
    </form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
    (function () {
        'use strict'
        var forms = document.querySelectorAll('.needs-validation')
        Array.prototype.slice.call(forms)
            .forEach(function (form) {
                form.addEventListener('submit', function (event) {
                    if (!form.checkValidity()) {
                        event.preventDefault()
                        event.stopPropagation()
                    }
                    form.classList.add('was-validated')
                }, false)
            })
    })()
</script>
</body>
</html>


Step 16: Create the VerifyEmail HTML file.

Go to src > main > resources > templates > verify-email.html and put the below HTML code.

HTML
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Verify Email</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<div class="container">
    <h2 class="mt-5">Verify Email</h2>
    <div th:if="${message}" class="alert" th:classappend="${message == 'Your account has been verified successfully.' ? 'alert-success' : 'alert-danger'}">
        <span th:text="${message}"></span>
    </div>
</div>
</body>
</html>


Step 17: Create the Login HTML file.

Go to src > main > resources > templates > login.html and put the below HTML code.

HTML
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Login</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<div class="container">
    <h2 class="mt-5">Login</h2>
    <form th:action="@{/login}" method="post" class="needs-validation" novalidate>
        <div class="mb-3">
            <label for="username" class="form-label">Email</label>
            <input type="text" class="form-control" id="username" name="username" required/>
            <div class="invalid-feedback">
                Please provide a valid email.
            </div>
        </div>
        <div class="mb-3">
            <label for="password" class="form-label">Password</label>
            <input type="password" class="form-control" id="password" name="password" required/>
            <div class="invalid-feedback">
                Please provide a password.
            </div>
        </div>
        <button type="submit" class="btn btn-primary">Login</button>
    </form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
    (function () {
        'use strict'
        var forms = document.querySelectorAll('.needs-validation')
        Array.prototype.slice.call(forms)
            .forEach(function (form) {
                form.addEventListener('submit', function (event) {
                    if (!form.checkValidity()) {
                        event.preventDefault()
                        event.stopPropagation()
                    }
                    form.classList.add('was-validated')
                }, false)
            })
    })()
</script>
</body>
</html>


pom.xml:

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>3.2.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.example</groupId>
    <artifactId>spring-security-account-activate-email</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-security-account-activate-email</name>
    <description>spring-security-account-activate-email</description>
    <properties>
        <java.version>17</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-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-mail -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
            <version>3.2.5</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.webjars/bootstrap -->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>5.3.3</version>
        </dependency>



        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity6</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </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>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>


Step 18: Run the Application

Now, we will run the application and it will start at port 8080.


Home page

Endpoint:

http://localhost:8080/

Output:


Register Page Endpoint:

http://localhost:8080/register

Output:


Enter the email and password and click on register then send the verification email to user email account.


Email verification:


Click on the link, then the below page will open. After that verify the account and finally account will be activated.

This project demonstrate how to implement the spring security to activate the new account by the email of the spring boot application.



Spring Security – Activate a New Account by Email

In Spring Security, Account activation by the email is the common feature in web applications to verify the authenticity of the users. It involves the sending the email with an activation link to the user during the registration process. The user must click this link to the activate their account and it can ensuring that the provided email address is valid.

The main concept of the email account activation in Spring Security involves the several key steps to the ensure the security and authenticity of the user accounts. Here the detailed explanation of the each step:

User Registration

  • The user fills out the registration form providing the necessary details such as the username, password and email address.
  • The server receives this information, creates the new user entity and saves it in database with the initial status indicating that the account is not yet activated.

Generate the Activation Token

  • Upon the registration, the server generates the unique activation token. This token is the randomly generated string that serves as the one time key for the account activation.
  • This token can be stored in the database, linked to newly created user entity. It can ensures that the each token is associated with the specific user.

Send the Activation Email

  • The email is sent to the users provided email address and it contains the activation link that includes the unique activation token.
  • The activation link can typically follows this format “http://www.exampledomain.com/verify?token=<activation_token>.
  • This email is crafted to include the personalized message and instructions on the how to activate the account by the clicking the link.

Activation Account

  • When the user can clicks the activation link in email then they are redirected to the specific endpoint in the server designed to handle the account activation of the user.
  • The server can retrieves the activation token from URL and searches the database for the matching token.
  • If the matching token is found then the server can verifies the associated user account by the setting its status to activate/enabled of the user account.
  • The token is then invalidation to prevent the reuse.
  • If token is invalid or expired then the appropriate the error message is displayed to the user.

Similar Reads

Implementation to Activate a New Account by Email in Spring Security

Below is the implementation to activate a new account by email in spring security....

Contact Us