September 30, 2025

Spring Boot – Password Reset

In this tutorial, you will learn how to implement Forget Password functionality in your SpringBoot application.

Spring Boot User Password Reset Workflow
Spring Boot User Password Reset Workflow

The Password Reset Flow

  1. From the login page, the user clicks on the ‘Forgot Password’ link.
  2. The request hits the /forgotPassword route in the SecurityController
  3. The forgotPassword.html template (which is in the security directory) is returned to the user. This template has a form with a single email field.
  4. User enters his email and submits the form.
  5. This new request arrives via the /passwordRequest route in the PasswordResetController
  6. The forgottenPassword(email) is invoked with the passed in user’s email
  7. If and email exists, then a token is generated and send along to the user via a password reset email
  8. User receives the email and clicks on the link in the email
  9. The request arrives via the /passwordChange route in the PasswordResetController
  10. The changePassword() method sends the user the password reset form along with the token. This form contains two fields: newPassword and confirmPassword
  11. User fills the form and submits via the /passwordChange route
  12. The updatePassword is called with the password and token and the user data is saved
  13. The user is redirected to the passwordChangeSuccessful page

 

Showing the  Forgot Password Form

Step 1 – Setup the /forgotPassword route

This is the first endpoint that would be hit when a user clicks on the Forgot Password link in the login form where the user must enter his email or username.

  1. Add the  forgotPassword() method to the SecurityController
@GetMapping("/forgotPassword")
public String forgotPassword(){
   return "security/forgotPassword";
}

 

2. Create the forgotPassword.html template in the security folder. This is the template that displays a form for use to enter his email. Get it here.

3. Update the AppSecurityConfig to allow the /forgotPassword route

4. Test(1) the /forgotPassword route

 

Sending the ForgetPassword Email

Step 2 – Create the ForgotPasswordEmailContext

This would be similar to the AccountVerificationEmail context but for password reset request. It will extend AbstractEmailContext.

The difference would be that it would use the forgot-password template from the mailing folder.  Do the following:

  • add the forgotPassword.html template to the security folder. Get it from here.
  • copy and modify the AccountVerificationEmailContext. The different could be in the verification url path and the name of course.

 

Step 3 – Setup the /passwordRequest Route and sendPasswordResetEmail Function

When the user enters his email in the forgotPassword.html form and submits, the request comes to this route. Do the following:

  1. Create and Implement the UserAccountService for managing password reset request. It would have two functions
    • forgottenPassword(username) for accepting the request containing the user email
    • updatePassword(password, token) for performing password update. Takes the new password and performs the update
  2. Write the sendResetPasswordEmail(user) function. This function takes the user as input and does the following:
    • create a secureToken
    • sets the user and saves the token
    • creates a new ForgottenPasswordEmailContext
    • sets the user and the token
    • sends the email

The sendResetPasswordResetEmail() is given below:

protected void sendResetPasswordEmail(User user) {
    SecureToken secureToken= secureTokenService.createSecureToken();
    secureToken.setUser(user);
    secureTokenRepository.save(secureToken);
    ForgotPasswordEmailContext emailContext = new ForgotPasswordEmailContext();
    emailContext.init(user);
    emailContext.setToken(secureToken.getToken());
    emailContext.buildVerificationUrl(baseURL, secureToken.getToken());
    try {
        emailService.sendMail(emailContext);
    } catch (MessagingException e) {
        e.printStackTrace();
    }
}

 

3. Override the forgottenPassword(user) function. It simply finds the user and sends the email

4. Add the forgot-password html template to the mailing folder. Get it from here.

5. Implement the /passwordRequest controller method. It’s named resetPassword. Here’s the implementation here:

@PostMapping("/passwordRequest") // Display form with only email field
public String resetPassword(final ResetPasswordData forgotPasswordForm, RedirectAttributes redirAttr) {
    try {
        customerAccountService.forgottenPassword(forgotPasswordForm.getEmail()); // Send email
    } catch (UnkownIdentifierException e) {
       // log the error
        redirAttr.addFlashAttribute("error",
                messageSource.getMessage("error", null, LocaleContextHolder.getLocale())
        );
    }
    redirAttr.addFlashAttribute("message",
            messageSource.getMessage("user.forgotpwd.msg", null, LocaleContextHolder.getLocale())
    );
    return REDIRECT_LOGIN;
}

 

6. Test(2) that email is raised by the /passwordRequest

 

Displaying the ChangePassword Form by Clicking the Link

Step 4 – Create the PasswordResetData and PasswordResetController

1. Create the PasswordResetData. This is the POJO for data used for password reset and would have  4 string fields:

  • email
  • token
  • password
  • repeatPassword

2. Create the PasswordResetController and write the controller method /passwordRequest. This method is named resetPassword and is given below:

@PostMapping("/passwordRequest")
public String resetPassword(final ResetPasswordData forgotPasswordForm, RedirectAttributes redirAttr) {
    try {
        customerAccountService.forgottenPassword(forgotPasswordForm.getEmail()); // Send email
    } catch (UnkownIdentifierException e) {
       // log the error
        redirAttr.addFlashAttribute("error",
                messageSource.getMessage("error", null, LocaleContextHolder.getLocale())
        );
    }
    redirAttr.addFlashAttribute("message",
            messageSource.getMessage("user.forgotpwd.msg", null, LocaleContextHolder.getLocale())
    );
    return REDIRECT_LOGIN;
}

 

3. Update the AppSecurityConfig to allow /passwordRequest route

4. Create the changePassword template in the security folder. Get it here

 

Step 5 – Setup the /passwordChange route for GET

This is the route that will be hit when the user clicks on the link in the email. The token is received and if it’s not empty, we create a ResetPasswordData form, sets the token and sends it to the user via the changePassword template.

1. Write the controller method for passwordChange. The method is given below:

@GetMapping("/passwordChange")
public String changePassword(@RequestParam(required = false) String token, final RedirectAttributes redirAttr, final Model model) {
    if (StringUtils.isEmpty(token)) {
        redirAttr.addFlashAttribute("tokenError",
                messageSource.getMessage("user.registration.verification.missing.token", null, LocaleContextHolder.getLocale())
        );
        return REDIRECT_LOGIN;
    }
    ResetPasswordData data = new ResetPasswordData();
    data.setToken(token);
    setResetPasswordForm(model, data); // add the resetPassword form to the model to send to the template
    return "security/changePassword";  
}

You will have an error at the setPasswordResetForm(model, data)

2. Create the setPasswordResetForm() function – it adds the passwordResetData to the model. The key is “forgotPassword”.

3. Update the AppSecurityConfig to allow /passwordChange route

4. Test(3) that the link in the email displays the password request form

 

Performing the Password Update (Fill and Submit new Password)

Step 7 – Setup the /passwordChange Route for Post

Here, the user enters and confirms the new password and clicks on submit.

  1. Override the updatePassword() method in the UserAccountService. You can uncomment the code and implement the method. This is given below:
@Override
public void updatePassword(String password, String token) throws InvalidTokenException, UnknownIdentifierException {
    SecureToken secureToken = secureTokenService.findByToken(token);
    
    if (Objects.isNull(secureToken) | !StringUtils.equals(token, secureToken.getToken()) | secureToken.isExpired()){
        throw new InvalidTokenException("Token does not exist");
    }
    
    User user = userRepository.getById(secureToken.getUser().getId());
    if(Objects.isNull(user)){
        throw new UnknownIdentifierException("User does not exist for this user");
    }
    
    user.setPassword(cryptPasswordEncoder.encode(password));
    userRepository.save(user);
    secureTokenService.removeToken(secureToken);
}

 

2. Write the changePassword() controller method. This would call the updatePassword method from the service.

@PostMapping("/passwordChange")
public String changePassword(final ResetPasswordData data, Model model) {
    try {
        userAccountService.updatePassword(data.getPassword(), data.getToken());
    } catch (InvalidTokenException | UnknownIdentifierException e) {
        e.printStackTrace();
        model.addAttribute("tokenError",
                messageSource.getMessage("user.registration.verification.invalid.token", null, LocaleContextHolder.getLocale()));
        return "security/changePassword";
    }
    model.addAttribute("passwordUpdatedMsg",
            messageSource.getMessage("user.password.updated.msg", null, LocaleContextHolder.getLocale()));
    setResetPasswordForm(model, new ResetPasswordData());
    return "security/passwordChangeSuccessful";
}

 

3. Create the passwordChangeSuccessful.html template in the security folder. Get it here.

4. Add the user.password.updated.msg to your messages.properties file

5. Test(4) that the password is updated after a new password is entered and submitted.

 

Happy coding!!

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments