Автор оригинала: Eugen Paraschiv.
1. Обзор
В этом уроке – мы продолжаем текущую Регистрацию с помощью Spring Security series с учетом основного “ Я забыл свой пароль ” функция – чтобы пользователь мог безопасно сбросить свой собственный пароль, когда ему это нужно.
2. Запросите оставшуюся часть Вашего пароля
Поток сброса пароля обычно начинается, когда пользователь нажимает какую-то кнопку “сброс” на странице входа в систему. Затем мы можем запросить у пользователя его адрес электронной почты или другую идентифицирующую информацию. После подтверждения мы можем сгенерировать токен и отправить электронное письмо пользователю.
Следующая диаграмма визуализирует поток, который мы реализуем в этой статье:
3. Токен Сброса Пароля
Давайте начнем с создания токена Сброса пароля сущности, чтобы использовать его для сброса пароля пользователя:
@Entity
public class PasswordResetToken {
private static final int EXPIRATION = 60 * 24;
@Id
@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;
}При сбросе пароля – будет создан токен и специальная ссылка, содержащая этот токен, будет отправлена пользователю по электронной почте .
Токен и ссылка будут действительны только в течение определенного периода времени (в данном примере 24 часа).
4. forgotPassword.html
Первая страница в этом процессе-это “ Я забыл свой пароль ” страница – где пользователю предлагается ввести свой адрес электронной почты, чтобы начать фактический процесс сброса.
Итак, давайте создадим простой forgotPassword.html запрос у пользователя адреса электронной почты:
reset
registration
login
Теперь нам нужно перейти по ссылке на эту новую страницу ” сброс пароля ” со страницы входа в систему:
5. Создайте токен сброса пароля
Давайте начнем с создания нового токена Сброса пароля и отправим его по электронной почте пользователю:
@PostMapping("/user/resetPassword")
public GenericResponse resetPassword(HttpServletRequest request,
@RequestParam("email") String userEmail) {
User user = userService.findUserByEmail(userEmail);
if (user == null) {
throw new UserNotFoundException();
}
String token = UUID.randomUUID().toString();
userService.createPasswordResetTokenForUser(user, token);
mailSender.send(constructResetTokenEmail(getAppUrl(request),
request.getLocale(), token, user));
return new GenericResponse(
messages.getMessage("message.resetPasswordEmail", null,
request.getLocale()));
}А вот метод create Password Reset Token For User() :
public void createPasswordResetTokenForUser(User user, String token) {
PasswordResetToken myToken = new PasswordResetToken(token, user);
passwordTokenRepository.save(myToken);
}А вот метод constructResetTokenEmail() – используется для отправки электронного письма с токеном сброса:
private SimpleMailMessage constructResetTokenEmail(
String contextPath, Locale locale, String token, User user) {
String url = contextPath + "/user/changePassword?token=" + token;
String message = messages.getMessage("message.resetPassword",
null, locale);
return constructEmail("Reset Password", message + " \r\n" + url, user);
}
private SimpleMailMessage constructEmail(String subject, String body,
User user) {
SimpleMailMessage email = new SimpleMailMessage();
email.setSubject(subject);
email.setText(body);
email.setTo(user.getEmail());
email.setFrom(env.getProperty("support.email"));
return email;
}Обратите внимание, как мы использовали простой объект Общий ответ для представления нашего ответа клиенту:
public class GenericResponse {
private String message;
private String error;
public GenericResponse(String message) {
super();
this.message = message;
}
public GenericResponse(String message, String error) {
super();
this.message = message;
this.error = error;
}
}6. Проверьте токен сброса пароля
Как только пользователь нажимает на ссылку в своем электронном письме, пользователь/меняет пароль конечная точка:
- проверяет, что токен действителен и
- предоставляет пользователю страницу обновить пароль , на которой он может ввести новый пароль
Новый пароль и токен затем передаются в конечную точку user/save Password :
Пользователь получает электронное письмо с уникальной ссылкой для сброса пароля и нажимает на ссылку:
@GetMapping("/user/changePassword")
public String showChangePasswordPage(Locale locale, Model model,
@RequestParam("token") String token) {
String result = securityService.validatePasswordResetToken(token);
if(result != null) {
String message = messages.getMessage("auth.message." + result, null, locale);
return "redirect:/login.html?lang="
+ locale.getLanguage() + "&message=" + message;
} else {
model.addAttribute("token", token);
return "redirect:/updatePassword.html?lang=" + locale.getLanguage();
}
}А вот метод validate Password Reset Token() :
public String validatePasswordResetToken(String token) {
final PasswordResetToken passToken = passwordTokenRepository.findByToken(token);
return !isTokenFound(passToken) ? "invalidToken"
: isTokenExpired(passToken) ? "expired"
: null;
}
private boolean isTokenFound(PasswordResetToken passToken) {
return passToken != null;
}
private boolean isTokenExpired(PasswordResetToken passToken) {
final Calendar cal = Calendar.getInstance();
return passToken.getExpiryDate().before(cal.getTime());
}7. Измените пароль
В этот момент пользователь видит простую страницу Сброса пароля , где единственным возможным вариантом является предоставление нового пароля :
7.1. updatePassword.html
reset
Обратите внимание, что мы показываем токен сброса и передаем его в качестве параметра POST в следующем вызове, чтобы сохранить пароль.
7.2. Сохраните пароль
Наконец, при отправке предыдущего запроса post – сохраняется новый пароль пользователя:
@PostMapping("/user/savePassword")
public GenericResponse savePassword(final Locale locale, @Valid PasswordDto passwordDto) {
String result = securityUserService.validatePasswordResetToken(passwordDto.getToken());
if(result != null) {
return new GenericResponse(messages.getMessage(
"auth.message." + result, null, locale));
}
Optional user = userService.getUserByPasswordResetToken(passwordDto.getToken());
if(user.isPresent()) {
userService.changeUserPassword(user.get(), passwordDto.getNewPassword());
return new GenericResponse(messages.getMessage(
"message.resetPasswordSuc", null, locale));
} else {
return new GenericResponse(messages.getMessage(
"auth.message.invalid", null, locale));
}
}А вот метод changeUserPassword() :
public void changeUserPassword(User user, String password) {
user.setPassword(passwordEncoder.encode(password));
repository.save(user);
}И Пароль к :
public class PasswordDto {
private String oldPassword;
private String token;
@ValidPassword
private String newPassword;
}
8. Заключение
В этой статье мы реализовали простую, но очень полезную функцию для зрелого процесса аутентификации – возможность сбросить свой собственный пароль, как пользователь системы.
полную реализацию этого учебника можно найти в проекте GitHub – это проект на основе Eclipse, поэтому его должно быть легко импортировать и запускать как есть.