The new Certification Class of Learn Spring Security is out:


1. Overview

This article continues the ongoing Registration with Spring Security series with one of the missing pieces of the registration process – verifying the user’s email to confirm their account.

The registration confirmation mechanism forces the user to respond to a “Confirm Registration” email sent after successful registration to verify his email address and activate their account. The user does this by clicking a unique activation link sent to them over email.

Following this logic, a newly registered user will not be able to log into the system until this process is completed.

2. A Verification Token

We will make use of a simple verification token as the key artifact through which a user is verified.

2.1. The VerificationToken Entity

The VerificationToken entity must meet the following criteria:

  1. It must link back to the User (via a unidirectional relation)
  2. It will be created right after registration
  3. It will expire within 24 hours following its creation
  4. Has a unique, randomly generated value

Requirements 2 and 3 are part of the registration logic. The other two are implemented in a simple VerificationToken entity like the one in Example 2.1.:

Example 2.1.

public class VerificationToken {
    private static final int EXPIRATION = 60 * 24;

    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String token;
    @OneToOne(targetEntity = User.class, fetch = FetchType.EAGER)
    @JoinColumn(nullable = false, name = "user_id")
    private User user;
    private Date expiryDate;
    private Date calculateExpiryDate(int expiryTimeInMinutes) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Timestamp(cal.getTime().getTime()));
        cal.add(Calendar.MINUTE, expiryTimeInMinutes);
        return new Date(cal.getTime().getTime());
    // standard constructors, getters and setters

Note the nullable = false on the User to ensure data integrity and consistency in the VerificationToken<->User association.

2.2. Add the enabled Field to User

Initially, when the User is registered, this enabled field will be set to false. During the account verification process – if successful – it will become true.

Let us start by adding the field to our User entity:

public class User {
    @Column(name = "enabled")
    private boolean enabled;
    public User() {

Note how we also set the default value of this field to false.

3. During Account Registration

Let’s add two additional pieces of business logic to the user registration use case:

  1. Generate the VerificationToken for the User and persist it
  2. Send out the email message for account confirmation – which includes a confirmation link with the VerificationToken’s value

3.1. Using a Spring Event to Create the Token and Send the Verification Email

These two additional pieces of logic should not be performed by the controller directly because they are “collateral” back-end tasks.

The controller will publish a Spring ApplicationEvent to trigger the execution of these tasks. This is as simple as injecting the ApplicationEventPublisher and then using it to publish the registration completion.

Example 3.1. shows this simple logic:

Example 3.1.

ApplicationEventPublisher eventPublisher

@RequestMapping(value = "/user/registration", method = RequestMethod.POST)
public ModelAndView registerUserAccount(
  @ModelAttribute("user") @Valid UserDto accountDto, 
  BindingResult result, 
  WebRequest request, 
  Errors errors) {
    if (result.hasErrors()) {
        return new ModelAndView("registration", "user", accountDto);
    User registered = createUserAccount(accountDto);
    if (registered == null) {
        result.rejectValue("email", "message.regError");
    try {
        String appUrl = request.getContextPath();
        eventPublisher.publishEvent(new OnRegistrationCompleteEvent
          (registered, request.getLocale(), appUrl));
    } catch (Exception me) {
        return new ModelAndView("emailError", "user", accountDto);
    return new ModelAndView("successRegister", "user", accountDto);

One additional thing to notice is the try catch block surrounding the publishing of the event. This piece of code will display an error page whenever there is an exception in the logic executed after the publishing of the event, which in this case is the sending of the email.

3.2. The Event and The Listener

Let’s now see the actual implementation of this new OnRegistrationCompleteEvent that our controller is sending out, as well as the listener that is going to handle it:

Example 3.2.1. – The OnRegistrationCompleteEvent

public class OnRegistrationCompleteEvent extends ApplicationEvent {
    private String appUrl;
    private Locale locale;
    private User user;

    public OnRegistrationCompleteEvent(
      User user, Locale locale, String appUrl) {
        this.user = user;
        this.locale = locale;
        this.appUrl = appUrl;
    // standard getters and setters

Example 3.2.2. The RegistrationListener Handles the OnRegistrationCompleteEvent

public class RegistrationListener implements 
  ApplicationListener<OnRegistrationCompleteEvent> {
    private IUserService service;
    private MessageSource messages;
    private JavaMailSender mailSender;

    public void onApplicationEvent(OnRegistrationCompleteEvent event) {

    private void confirmRegistration(OnRegistrationCompleteEvent event) {
        User user = event.getUser();
        String token = UUID.randomUUID().toString();
        service.createVerificationToken(user, token);
        String recipientAddress = user.getEmail();
        String subject = "Registration Confirmation";
        String confirmationUrl 
          = event.getAppUrl() + "/regitrationConfirm.html?token=" + token;
        String message = messages.getMessage("message.regSucc", null, event.getLocale());
        SimpleMailMessage email = new SimpleMailMessage();
        email.setText(message + " rn" + "http://localhost:8080" + confirmationUrl);

Here, the confirmRegistration method will receive the OnRegistrationCompleteEvent, extract all the necessary User information from it, create the verification token, persist it, and then send it as a parameter in the “Confirm Registration” link.

As was mentioned above, any javax.mail.AuthenticationFailedException thrown by JavaMailSender will be handled by the controller.

3.3. Processing the Verification Token Parameter

When the user receives the “Confirm Registration” link they should click on it.

Once they do – the controller will extract the value of the token parameter in the resulting GET request and will use it to enable the User.

Let’s see this process in Example 3.3.1.:

Example 3.3.1. – RegistrationController Processing the Registration Confirmation

private IUserService service;

@RequestMapping(value = "/regitrationConfirm", method = RequestMethod.GET)
public String confirmRegistration
  (WebRequest request, Model model, @RequestParam("token") String token) {
    Locale locale = request.getLocale();
    VerificationToken verificationToken = service.getVerificationToken(token);
    if (verificationToken == null) {
        String message = messages.getMessage("auth.message.invalidToken", null, locale);
        model.addAttribute("message", message);
        return "redirect:/badUser.html?lang=" + locale.getLanguage();
    User user = verificationToken.getUser();
    Calendar cal = Calendar.getInstance();
    if ((verificationToken.getExpiryDate().getTime() - cal.getTime().getTime()) <= 0) {
        String messageValue = messages.getMessage("auth.message.expired", null, locale)
        model.addAttribute("message", messageValue);
        return "redirect:/badUser.html?lang=" + locale.getLanguage();
    return "redirect:/login.html?lang=" + request.getLocale().getLanguage(); 

The user will be redirected to an error page with the corresponding message if:

  1. The VerificationToken does not exist, for some reason or
  2. The VerificationToken has expired

See Example 3.3.2. to see the error page.

Example 3.3.2. – The badUser.html

    <h1 th:text="${param.message[0]}>Error Message</h1>
    <a th:href="@{/registration.html}" 

If no errors are found, the user is enabled.

There are two opportunities for improvement in handling the VerificationToken checking and expiration scenarios:

  1. We can use a Cron Job to check for token expiration in the background
  2. We can give the user the opportunity to get a new token once it has expired

We’ll defer the generation of a new token for a future article and assume that the user does indeed successfully verify their token here.

4. Adding Account Activation Checking to the Login Process

We need to add the code that will check if the user is enabled:

Let’s see this in Example 4.1. which shows the loadUserByUsername method of MyUserDetailsService.

Example 4.1.

UserRepository userRepository;

public UserDetails loadUserByUsername(String email) 
  throws UsernameNotFoundException {
    boolean enabled = true;
    boolean accountNonExpired = true;
    boolean credentialsNonExpired = true;
    boolean accountNonLocked = true;
    try {
        User user = userRepository.findByEmail(email);
        if (user == null) {
            throw new UsernameNotFoundException(
              "No user found with username: " + email);
        return new
    } catch (Exception e) {
        throw new RuntimeException(e);

As we can see, now MyUserDetailsService not uses the enabled flag of the user – and so it will only allow enabled the user to authenticate.

Now, we will add an AuthenticationFailureHandler to customize the exception messages coming from MyUserDetailsService. Our CustomAuthenticationFailureHandler is shown in Example 4.2.:

Example 4.2. – CustomAuthenticationFailureHandler:

public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    private MessageSource messages;

    private LocaleResolver localeResolver;

    public void onAuthenticationFailure(HttpServletRequest request, 
      HttpServletResponse response, AuthenticationException exception)
      throws IOException, ServletException {

        super.onAuthenticationFailure(request, response, exception);

        Locale locale = localeResolver.resolveLocale(request);

        String errorMessage = messages.getMessage("message.badCredentials", null, locale);

        if (exception.getMessage().equalsIgnoreCase("User is disabled")) {
            errorMessage = messages.getMessage("auth.message.disabled", null, locale);
        } else if (exception.getMessage().equalsIgnoreCase("User account has expired")) {
            errorMessage = messages.getMessage("auth.message.expired", null, locale);

        request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, errorMessage);

We will need to modify login.html to show the error messages.

Example 4.3. – Display error messages at login.html:

<div th:if="${param.error != null}" 

5. Adapting the Persistence Layer

Let’s now provide the actual implementation of some of these operations involving the verification token as well as the users.

We’ll cover:

  1. A new VerificationTokenRepository
  2. New methods in the IUserInterface and its implementation for new CRUD operations needed

Examples 5.1 – 5.3. show the new interfaces and implementation:

Example 5.1. – The VerificationTokenRepository

public interface VerificationTokenRepository 
  extends JpaRepository<VerificationToken, Long> {

    VerificationToken findByToken(String token);

    VerificationToken findByUser(User user);

Example 5.2. – The IUserService Interface

public interface IUserService {
    User registerNewUserAccount(UserDto accountDto) 
      throws EmailExistsException;

    User getUser(String verificationToken);

    void saveRegisteredUser(User user);

    void createVerificationToken(User user, String token);

    VerificationToken getVerificationToken(String VerificationToken);

Example 5.3. The UserService

public class UserService implements IUserService {
    private UserRepository repository;

    private VerificationTokenRepository tokenRepository;

    public User registerNewUserAccount(UserDto accountDto) 
      throws EmailExistsException {
        if (emailExist(accountDto.getEmail())) {
            throw new EmailExistsException(
              "There is an account with that email adress: " 
              + accountDto.getEmail());
        User user = new User();
        user.setRole(new Role(Integer.valueOf(1), user));

    private boolean emailExist(String email) {
        User user = repository.findByEmail(email);
        if (user != null) {
            return true;
        return false;
    public User getUser(String verificationToken) {
        User user = tokenRepository.findByToken(verificationToken).getUser();
        return user;
    public VerificationToken getVerificationToken(String VerificationToken) {
        return tokenRepository.findByToken(VerificationToken);
    public void saveRegisteredUser(User user) {;
    public void createVerificationToken(User user, String token) {
        VerificationToken myToken = new VerificationToken(token, user);;

6. Conclusion

In this article, we’ve expanded the registration process to include an email based account activation procedure.

The account activation logic requires sending a verification token to the user via email so that they can send it back to the controller to verify their identity.

The implementation of this Registration with Spring Security tutorial can be found in the GitHub project – this is an Eclipse based project, so it should be easy to import and run as it is.

Go deeper into Spring Security with the course:


  • XOR

    I know that there is a good reason, but can you explain why you use an eventPublisher for event OnRegistrationCompleteEvent ? Why not put directly the mail sender in the registerUserAccount method (/user/registration) ? Is-it to prevent error during the mail delivery and then resend mail (asynchronous treatment) ? Thanks

    • Guest

      Hey XOR,

      I think I can answer your question:
      This is basically a design decision. You want to decouple the controller’s responsibility from the the email sending logic.This makes sense for 2 reasons:
      1.We have an implementation independent listener. So, if we later need to change how a user should be alerted to confirm his account all we need is to change is the listener’s implementation.
      2.The controller’s logic should be kept simple and centered in processing requests and handling DTO objects. Keep in mind that the VerificationToken is not part of the DTO object.
      I hope this makes sense.

      • XOR

        Hi Elena, thank you for your answer, your explaination is very interesting !

        • The don

          Hello Elena,

          Thanks for your response.
          However, I don’t seem to understand the rational behind the controller handling any exception thrown by the JavaMailSender.
          I thought one of reasons for using an Event was to make the Sending of the Mail Asynchronous or at least letting the MailSender run it its own Thread

          • Guest


            The exception iis purely a usability detail. What if something goes wrong when sending the email? As a mater of fact, when I was testing it I had forgotten to configure the sender’s e-mail account and I got a general exception with an ugly, long stack trace.
            It is true, we could have gone asynchronous, and it sounds like a great idea for a future article, but we would still have to handle email sending problems. Also, this is a short article, and focusing too much on the email sending mechanics distracts the user from the main theme of the article: Account confirmation (in this case via email).

          • The don

            Hello Elena,

            Thanks for your response.
            I wasn’t focusing on the Email Sending. I Just really loved the idea of using Events. And I’m already thinking of using it in a project I’m currently working on.
            It is just that I wanted a good reason to let me do the exception Handling in the Controller. You are right; We still have to handle the email sending problems. But should we report to the user that the email sending failed? As a matter of fact it could fail for so many reasons.
            Once more thanks.

          • Elena Eidson

            Yes, there are multiple reasons why it could fail, but the owner of the system (administrator)) HAS to KNOW it failed the minute it fails-thus,the controller should know about it.
            Now, if the user should be notified, that depends on the specific requirements.If system responsiveness and speed in all processes is crucial then it is a good idea to inform the user that the system delayed a response due to a problem sending the email.If a security breach is suspected as the cause of the failure then the user should be notified too.

          • Dan

            What are the pro and cons of versus using TransactionSynchronizationAdapter, when upon transaction successfully of the user creation then you send out the email with token.

          • I haven’t used that class in quite a number of years Dan 🙂 – I’m not sure it’s maintained any more. However, you can certainly use a TransactionCallback if you want to do that, sure.
            Hope that helps. Cheers,

    • Nice Post Baeldung

      Nice Question XOR, I was wondering that while I read it as well, though its a good design.

      It’s not just the objects the controller knows about but also the processing, the controller now just needs to know what it has done, not what should happen next, so it makes it much more pluggable to put different behaviour when a registration event is complete.

      Also as your question hints at, it would be very easy to extend this to say send the emails async, with retries.

      The only downside I can see is if you wanted to tell the user that sending the email failed, you have lost that … but I probably wouldn’t do that anyway.

      • Hey Steve,
        One quick note on the “telling the user the email sending failed” – in my view, that’s not the kind of detail the user needs to know. If the email sending failed for some reason, Ops needs to be aware and notified, fix the problem and resend. The whole process needs to be transparent for the user – since there’s nothing they can do about it. Hope that makes sense. Cheers,

        • Damilola Ajiboye


  • Hariharan Narayanan

    This post gave me a good primer on both (a) Implementing email based workflows and (b) Event handling using Spring. Thanks indeed!

  • The Alchemist

    typo at “User registered = registered = createUserAccount(accountDto);”

    • That’s a nice catch – I don’t know how that got in there. Fixed, thanks.

  • Jim Clayson

    Hey Eugen.

    I have a problem extracting the context path i.e. String appUrl = ‘request.getContextPath()’, in RegistrationController, for use in the emailed link.

    I get a blank string back. Any clues as to why that might be? Or is that question out of scope for this article?

    The only way I can get the protocol, host, port is via httpServletRequest. Which is no problem for me but I was expecting your use of WebRequest to return a value.

    Great work with this series, btw.

    • Hey Jim,
      It depends on how you’re deploying the app. My suggestion is to debug through it. The raw information is there, so you’ll have to see exactly where that’s lost and why getContextPath() isn’t able to gathering it and return it.
      Finally, the HttpServletRequest has a few other methods that may be able to extract similar data, so it’s worth having a look there as well.
      Glad you’re finding the series useful of course.

  • Raghave

    Very Well Done ! I have not implemented this yet but seems very well presented. I see that your articles have amazing clarity. To further improve, i would suggest adding small flowchart / activity diagram to explain the scenario that you are going to explain would really make it even better. Even without it its very well presented. Thats why you are not only a spring evangelist , you are an Icon.

    • That’s an interesting idea Reghave – I’ll keep that in mind to update the article.
      Glad you’re enjoying the site BTW. Cheers,

  • Praveen Shethe

    Very nice presented, Thanks indeed

  • Arthur Welsch

    What font you use for your code, it’s really pretty

    • Hey Arthur – the font is called Raleway – I’m glad you like it 🙂

  • NathanR


    Thanks for a great tutorial.

    I used it for a Spring web app I’m working on and it worked great.

    I did notice that now the account registration process takes noticeably longer.

    I put a timer in the controller before and after the call to the publish event and it came out 3957 milliseconds! (on my local PC)

    I am still a beginner so may be a silly question but is there a way to have the send email part work on a separate thread? is it safe? OK?

    Thanks in advance

    • Definitely not a silly question. Yes, on a production implementation, I would definitely with @Async or a similar implementation, where the user doesn’t have to wait for the actual email to be sent.
      I didn’t introduce it here because it’s unrelated to the actual logic and I didn’t want to overly complicate things.
      Hope that helps. Cheers,

      • NathanR

        Thanks for the quick reply. Since I intend to have my site on production I will need this.
        I noticed you wrote a tutorial for @Async so I will try to implement it on my site.

  • Eric W

    Example 4.1 Line 24:
    This is a typo, right?

    • Hey Eric – nice catch. Yes it is – fixed.

  • akuma8

    Thanks a lot for this nice tutorial. I am trying to perform the same in my application but there are some points that I don’t understand. I am learning Spring and J2EE at same time so could you please enlighten me on some points?
    Where did you implement the methods of the interfaces in the package “com.baeldung.persistence.dao” (e.g. findByToken(String token);) I downloaded the project from GitHub but I didn’t find it.
    Is there the code of SQL tables (VerifcationToken and User) in GitHub?
    Another question :
    when is “onApplicationEvent(OnRegistrationCompleteEvent event)” called?

    I ask this question because “RegistrationListener” is never instanciated (or the calling of this method totally transparent for the developper)

    Is there an XML version?

    Thanks a lot.

    • The project is using Spring Data, so there’s no explicit implementation, the framework generates one for us.
      The DB structure is also generated.
      The listener isn’t called explicitly either – I’m using the Spring event support here, so the event is sent and then the framework calls the listener to handle it.
      Finally – the project is doing Java config, so no XML.
      Hope that clears things up 🙂

      • akuma8

        Thanks a lot for all your tutorials, you push me to learn new things. I didn’t know how Spring Data works and thanks to you I learn it and it is really powerful.
        Thanks again to take time to reply.

        • Happy to help Akuma. Cheers,

          • akuma8

            How long did take you to control Spring and Java? I am curious ^^

          • That’s an interesting question. I would say I was building stuff after a few months, but of course to understand things in depth takes a lot longer. Naturally, you can still build useful things as you do.

  • Eric W

    Once this feature is enabled, how could we change

    to control access based on the value of enabled?

    • Hey Eric,
      You can define the authorization rules like that, by URL, but you can also be more granular. You can define more granular URLs if you need to, or you can use method based security with annotations.
      The point is – the framework is pretty flexible and so you can go broad or very granular, depending on what you need.

      • Eric W

        I think my question may not be very clear.
        I think this original rule applies whether or not the user is enabled because it is created with ROLE_USER (but not enabled). So in this case a user will have access to those pages even before they click the link to verify their account.
        Something must be changed so that we can distinguish the “enabled” column, or not?

        • So, to clarify – when you say “enabled” – do you mean the enabled flag of the User in Spring Security?

          • Eric W

            I mean the result of activating the account via clicking the link in the email. If I understand correctly, that sets the enabled flag of the user in spring security, right? How does the login page distinguish such status? Thanks!

          • OK, that’s what I was thinking you meant. That means you simply need to use authenticated when defining the authorization semantics of that URL space. That means that Spring Security will simply require the user to be authenticated – nothing more. And so, yes – if the user isn’t enabled – it will not have access there.

  • Tom

    Many thanks for that article, great stuff !
    Btw, can’t calculateExpiryDate() method’s content be replaced with

    Calendar cal = Calendar.getInstance();
    cal.add(Calendar.HOUR, 24);
    return cal.getTime();

    • Tom

      Didn’t see that you’re passing an argument to the method, sorry for that.
      Anyway, I guess that setTime(…) can be omitted.

  • Sudip Nayak

    Why m not getting registration confirmation link:
    I was working on registration part, fork the repo, added the missing block. For each time I do a registration I got up to this:

    You registered successfully. We will send you a confirmation message to your email account.

    But I never receive any confirmation email. Even I tried above url to get the confirmation, but encountered with an error page. So please tell me what’s missing n where I need to look to fix the issue.

    M using H2 db for test and FAKE SMTP for mail communication.

    • Hey Sudip – I imagine your SMTP settings aren’t correct, and so you’re probably not sending email. Which – given you’re using Fake SMTP – is not unexpected. That library – as the name suggests – doesn’t actually allow you to send email, and is primarily used for testing.
      So – basically – make sure you configure a real SMTL server that’s able to send (GMail, Amazon SES, etc).
      Hope that helps. Cheers,

      • Sudip Nayak

        Thanks for suggestions. could you please help in setting up the smtp for gmail, although I tried to set it by modifying the properties file but m getting connection timed out error.

        [email protected]
        [email protected]

        • Grzegorz Piwowarek

          This looks like an issue with the server itself. Are you using your own or Gmail for example?

          • Sudip Nayak

            I am using Gmail server for example. Tried to get the set configuration but nothing seems working. Do you have any alt solution?

          • Sure – Amazon SES is a good way to go. But – at the end of the day – any valid SMTP server should be fine – as long as it’s properly configured.
            Hope that helps. Cheers,

          • Sudip Nayak

            At this point I would like to try the gmail configuration. Eugen, could you please help me to set the valid smtp configuration for gmail.

          • Grzegorz Piwowarek

            Sudip, try this link, should be helpful

  • Saravana Kumar

    Nice tutorial it helped for me. I implemented this in my project… Thanks!