DEV Community

APD
APD

Posted on • Originally published at Medium on

Account activation by sending email confirmation with Java Spring MVC

In this article, you will learn how to implement user registration with email confirmation. Basically, after the registration process, a new user is created behind the scenes. But the user cannot log into the application as the account is by default locked. To activate the user account we need a confirmation from the user. That is why we send a link to verify the registration process and activate the user account if the user clicks on the link.

Assuming that you already know how to register a new user, we are now heading towards the email verification part. (In case you don’t have any idea about how to register a new user, you may have a look at the GitHub repository mentioned at the end of this article).

We are going to modify few files that we have already created while implementing the user registration process. Apart from this, we are going to add the following new files to our project.

  • Entity  — VerificationToken.java
  • Repository layers  — TokenDAO.java, ITokenDAO.java
  • Package  — event
  • event > Event  — OnRegistrationSuccessEvent.java
  • event > EventListener  — RegistrationEmailListener.java

First we are going to start with our AccountController.java. The user fills the registration form and clicks submit. Onsubmit, the User object with the data is posted to the registerNewUser method with the following method signature.

AccountController.java

public String registerNewUser(@ModelAttribute(“user”) UserDTO userDto, BindingResult result, WebRequest request, Model model) { … }
Enter fullscreen mode Exit fullscreen mode

Note : Please ensure that you give the parameters in the same order as above. Changing this order may give you some unwanted errors.

Always make sure that you do not use your entity directly on your controller as it may put your application into security vulnerabilities. (i.e., you may expose the entire domain model to the client. We really don’t want this to happen). So make use of a Data Transfer Object (DTO) as a middle layer to acces the entity.

We recall few steps from the registration process here,

  • Initialize a new User object.
  • Find that the user already exists.
  • If user already exists, we show an error message else we create a new user and assign the user to the newly initialized User object

AccountController.java

User registeredUser = new User();

String userName = userDto.getUserName();

if (result.hasErrors()) {

return “registration”;

}

registeredUser = service.findByUsername(userName);

if(registeredUser!=null) {

model.addAttribute(“error”,”There is already an account with this username: “ + userName);

return “registration”;

}

registeredUser = service.registerUser(userDto);
Enter fullscreen mode Exit fullscreen mode

Now let’s move into the confirmation of user via email.

Before going to the controller code, we have to first create the Event and EventListener in order to send the email on successful registration of the user.

This will be our event,

OnRegistrationSuccessEvent.java

public class OnRegistrationSuccessEvent extends ApplicationEvent {

private static final long serialVersionUID = 1L;

private String appUrl;

private User user;

public OnRegistrationSuccessEvent(User user, String appUrl) {

super(user);

this.user = user;

this.appUrl = appUrl;

}

public String getAppUrl() {

return appUrl;

}

public void setAppUrl(String appUrl) {

this.appUrl = appUrl;

}

public User getUser() {

return user;

}

public void setUser(User user) {

this.user = user;

}
Enter fullscreen mode Exit fullscreen mode

Events must always inherit the ApplicationEvent class of Spring that is inherited from the java.util.EventObject. We have three variables serialVersionUID, appurl and user.

It is strongly recommended that all serializable classes explicitly declare serialVersionUID values, otherwise it may result in unexpected InvalidClassExceptions during deserialization. To know more visit the Java docs for java.io.Serializable

appurl is the context path to our application and user is the new user object. These two variables construct the confirmation url. However, we do not use the serialVersionUID in our url. And finish the Event creation with the implementation of the basic getters/setters and constructors.

Next is we have to build an EventListener to handle the OnRegistrationSuccessEvent. We name our EventListener as RegistrationEmailListener.java and give it the @Component annotation. This will be responsible for sending our confirmation email to the newly registered user. The RegistrationEmailListener inherits the ApplicationListener that Listens to our OnRegistrationSuccessEvent event.

@Component

public class RegistrationEmailListener implements ApplicationListener\<OnRegistrationSuccessEvent\> { … }
Enter fullscreen mode Exit fullscreen mode

Inside this class, we will do the Dependency Injection for the IUserService and MailSender with the @Autowired annotaion of Spring.

@Autowired

private IUserService userService;

@Autowired

private MailSender mailSender;
Enter fullscreen mode Exit fullscreen mode

Then we will override onApplicationEvent , the method defined by the org.springframework.context.ApplicationListener. Inside this we are going to call the confirmRegistration method that we will create in the next few steps.

@Override

public void onApplicationEvent(OnRegistrationSuccessEvent event) {

this.confirmRegistration(event);

}
Enter fullscreen mode Exit fullscreen mode

The confirmRegistration method takes OnRegistrationSuccessEvent as its parameter.

private void confirmRegistration(OnRegistrationSuccessEvent event) { … }
Enter fullscreen mode Exit fullscreen mode

Then we will initialize the user with the passed parameter. Each user must have a unique id because at this point we are still unaware that the new user have been already registered. For this, we create a unique token using the UUID. Using this UUID, we are going to create a verification token along with an expiration date and save it to the VerificationToken table in the database. When the user clicks the link, the verification token helps us to identify whether the confirmation link is valid and is not expired. We create this token in the createVerificationToken method implemented in the UserService.

RegistrationEmailListener.java

User user = event.getUser();

String token = UUID.randomUUID().toString();

userService.createVerificationToken(user,token);
Enter fullscreen mode Exit fullscreen mode

IUserService.java

public void createVerificationToken(User user, String token);
Enter fullscreen mode Exit fullscreen mode

UserService.java

@Override 
public void createVerificationToken(User user, String token) { VerificationToken newUserToken = new VerificationToken(token, user); tokenDAO.save(newUserToken); 
}
Enter fullscreen mode Exit fullscreen mode

After saving the verification token, we will now generate the confirmation email that has to be sent to the user. Get the recipient from the user object. Then set the email subject text to whatever you want. But I recommend you to give a subject related to the email message. (i.e., Registration Confirmation.) Then we create the url by concatenating the appurl with the route to the method that activates the user and with the user token (i.e., UUID ). We will then generate the message to be sent along with the email.

String recipient = user.getEmail();

String subject = “Registration Confirmation”;

String url

= event.getAppUrl() + “/confirmRegistration?token=” + token;

String message = “Thank you for registering. Please click on the below link to activate your account.”;
Enter fullscreen mode Exit fullscreen mode

Now we have all the necessary data and we are going to send the email using the SimpleMailMessage.

SimpleMailMessage email = new SimpleMailMessage();

email.setTo(recipient);

email.setSubject(subject);

email.setText(message + “http://localhost:8080" + url);

mailSender.send(email);
Enter fullscreen mode Exit fullscreen mode

Create the SimpleMailMessage bean and set the SMTP mail settings in the Config.java file.

@Bean(name = “mailSender”)

public MailSender javaMailService() {

JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();

javaMailSender.setHost(“smtp.gmail.com”);

javaMailSender.setPort(587);

javaMailSender.setProtocol(“smtp”);

javaMailSender.setUsername(“sender’s email”);

javaMailSender.setPassword(“sender’s password”);

Properties mailProperties = new Properties();

mailProperties.put(“mail.smtp.auth”, “true”);

mailProperties.put(“mail.smtp.starttls.enable”, “true”);

mailProperties.put(“mail.smtp.debug”, “true”);

javaMailSender.setJavaMailProperties(mailProperties);

return javaMailSender;

}

@Bean

public MessageSource messageSource() {

final ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();

messageSource.setBasename(“classpath:messages”);

messageSource.setUseCodeAsDefaultMessage(true);

messageSource.setDefaultEncoding(“UTF-8”);

messageSource.setCacheSeconds(0);

return messageSource;

}
Enter fullscreen mode Exit fullscreen mode

Now we have to create the VerificationToken Entity inside the entity package of your application.

VerificationToken.java

@Entity

@Table(name = “verification\_token”)

public class VerificationToken {

private static final int EXPIRATION = 60 \* 24;

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private int id;

@Column(name=”token”)

private String token;

@OneToOne(targetEntity = User.class, fetch = FetchType.EAGER)

@JoinColumn(name = “user\_id”, nullable = false)

private User user;

@Column(name=”created\_date”)

private Date createdDate;

@Column(name=”expiry\_date”)

private Date expiryDate;
Enter fullscreen mode Exit fullscreen mode

Generate the basic getters/setters and the constructors for the entity. We will create the calculateExpiryDate method to calculate the expiration date of the verification token.

VerificationToken.java

private Date calculateExpiryDate(int expiryTimeInMinutes) {

Calendar calendar = Calendar.getInstance();

calendar.setTime(new Timestamp(calendar.getTime().getTime()));

calendar.add(Calendar.MINUTE, expiryTimeInMinutes);

return new Date(calendar.getTime().getTime());

}
Enter fullscreen mode Exit fullscreen mode

Then we are going to initiate the publishEvent in order to send the email to the user. After the email has successfully sent, the user will receive a success message.

AccountController.java

try {

String appUrl = request.getContextPath();

eventPublisher.publishEvent(new OnRegistrationSuccessEvent(registeredUser, appUrl));

}catch(Exception re) {

re.printStackTrace();

}

return “registrationSuccess”;
Enter fullscreen mode Exit fullscreen mode

The user receives an email with the following format,

Thank you for registering. Please click on the below link to activate your account. http://localhost:8080/spring-email-verification/confirmRegistration?token=3e5a64c7-dbb1-4179-be48-be9361703457

After the user clicks the link:

Now when the user clicks the link, he/she gets directed to the path /confirmRegistration .

AccountController.java

@GetMapping("/confirmRegistration")

public String confirmRegistration(WebRequest request, Model model,@RequestParam("token") String token) {

VerificationToken verificationToken = service.getVerificationToken(token); 
 if(verificationToken == null) { 
 String message = messages.getMessage("auth.message.invalidToken", null, locale); 
 model.addAttribute("message", message); 
 return "redirect:access-denied"; 
 }

User user = verificationToken.getUser(); 
 Calendar calendar = Calendar.getInstance(); 
 if((verificationToken.getExpiryDate().getTime()-calendar.getTime().getTime())\<=0) { 
 String message = messages.getMessage("auth.message.expired", null, locale); 
 model.addAttribute("message", message); 
 return "redirect:access-denied"; 
 }

user.setEnabled(true); 
 service.enableRegisteredUser(user); 
 return null; 
}
Enter fullscreen mode Exit fullscreen mode

Here the GET method is called and it first checks whether the token is valid. If not valid, we are going to pass an error stating that the link is invalid. Then we will check the expiration of the token. If the link is valid in both the cases, we will activate the user with the setteruser.setEnabled(true).

Finally, in the UserService we will update the database with the new values.

IUserService.java

public void enableRegisteredUser(User user);
Enter fullscreen mode Exit fullscreen mode

UserService.java

@Override 
@Transactional 
public void enableRegisteredUser(User user) { 
 userDAO.save(user); 
}
Enter fullscreen mode Exit fullscreen mode

So, we have successfully implemented the user account confirmation process with Spring. You can find the complete code in this Github repository.

Thank you for visiting my blog :)

Top comments (0)