Here I am going to share how to implement forgotten passwords in Spring Boot projects. I will share a simple implementation where the authenticity of the user is being taken care of by the validity of the email address of the user.
Creating Controllers
In your controller
create two @PostMapping
routes/endpoints as shown below.
@PostMapping("/forgot_password")
public ResponseMessage processForgotPassword(@RequestBody PasswordResetRequest passwordResetRequest) {
return passwordPolicyService.updateResetPasswordToken( passwordResetRequest );
}
@PostMapping("/reset_password")
public ResponseMessage processResetPassword(@RequestBody ResetPasswordRequest request) {
return passwordPolicyService.getByResetPasswordToken( request );
}
In the two @PostMapping
I have shown above, I have used my own library (referred to as DTO) to return data from these two methods. The ResponseMessage
looks something like this:
package com.bhutanio.myproject.lib;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResponseMessage {
private Integer status;
private String text;
}
In the Java class shown above, I have Lombok annotations. If you are not aware of it, I have shared it in another post.
Another DTO used in this implementation is ResetPasswordRequest.
package com.bhutanio.myproject.lib;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResetPasswordRequest {
private String token;
private String password;
}
The first method processForgotPassword
expects an email address from a client like this:
processResetPassword
expects request from a client like this:
Creating Service Methods
From the two controller methods, it is vivid that we need two service methods.
Here are my service methods:
public ResponseMessage updateResetPasswordToken(PasswordResetRequest passwordResetRequest)
throws UserNotFoundException {
String email = passwordResetRequest.getEmail();
String token = RandomString.make(30);
String path = environment.getProperty("endpointUrl");
try {
String resetPasswordLink = path + "/reset-password?token=" + token;
mailSenderService.sendEmail(email, "Forgot password link", resetPasswordLink);
User user = userRepository.findByEmailAddr(email);
if (user != null) {
user.setResetPasswordToken(token);
userRepository.save(user);
}
} catch (UserNotFoundException ex) {
System.out.println( ex );
responseMessage.setStatus(SUCCESSFUL_STATUS);
responseMessage.setText("Please check your email inbox for password reset instructions.");
return responseMessage;
}
responseMessage.setStatus(SUCCESSFUL_STATUS);
responseMessage.setText("Please check your email inbox for password reset instructions.");
return responseMessage;
}
public ResponseMessage getByResetPasswordToken(ResetPasswordRequest request) {
String token = request.getToken();
String password = request.getPassword();
User user = userRepository.findByResetPasswordToken(token);
if (user == null) {
responseMessage.setStatus(UNSUCCESSFUL_STATUS);
responseMessage.setText("You've encountered some errors while trying to reset your password.");
return responseMessage;
} else {
updatePassword(user, password);
}
responseMessage.setStatus(SUCCESSFUL_STATUS);
responseMessage.setText("You've successfully reset your password.");
return responseMessage;
}
These two methods expect the implementation of JavaMailSender
in a service named mailSenderService
as shown above.
updateResetPasswordToken
Firstly, the user must send an email address via @PostMapping("/forgot_password")
using PasswordResetRequest
DTO.
String token = RandomString.make(30);
This line of code creates an instance of RandomString
of 30 characters.
We get the appropriate environment URL and create a link to hit the @PostMapping("/reset_password")
. After creating the link, try sending the link to the email address the user has requested.
The random string (token) is being saved in the database so that it can be used to check whether the user is trying with a valid token or hitting with random string trails. This is a very important point to note while implementing this type of forgot password feature.
If you look carefully, you must have seen that the response messages are the same both when email sending succeeds or fails. This is to assure that the user does not try again and again with email addresses that are not present in the database.
getByResetPasswordToken
PasswordResetRequest
must contain a valid token and a password string. getByResetPasswordToken
method is triggered by the link that the user has received in his/her email.
If the token is valid, the new password is encoded with BCryptPasswordEncoder
, password token is reset to null and saved in the database.
updatePassword
This is how updatePassword
the method looks like. This method is called if the token the user sent to @PostMapping("/forgot_password")
is correct.
public void updatePassword(User user, String newPassword) {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encodedPassword = passwordEncoder.encode(newPassword);
user.setPassword(encodedPassword);
user.setResetPasswordToken(null);
// other implementations if required
userRepository.save(user);
}
Hope this helps you in making your dream project come true!