Рубрики
Без рубрики

Spring Security – Сбросьте Свой Пароль

Каждое приложение должно позволять пользователям изменять свой собственный пароль в случае, если они его забудут.

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