Автор оригинала: 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, поэтому его должно быть легко импортировать и запускать как есть.