Перейти к основному содержимому

Регистрация Spring Security — повторно отправить электронное письмо с подтверждением

· 4 мин. чтения

1. Обзор

В этом руководстве мы продолжаем текущую серию « Регистрация в Spring Security » и рассмотрим повторную отправку пользователю ссылки для подтверждения в случае, если срок ее действия истечет до того, как у него появится возможность активировать свою учетную запись.

2. Повторно отправьте ссылку для подтверждения

Во-первых, давайте посмотрим, что происходит, когда пользователь запрашивает другую ссылку для подтверждения, если срок действия предыдущей истек.

Во-первых, мы сбросим существующий токен с новым expireDate . Мы отправим пользователю новое электронное письмо с новой ссылкой/токеном:

@GetMapping("/user/resendRegistrationToken")
public GenericResponse resendRegistrationToken(
HttpServletRequest request, @RequestParam("token") String existingToken) {
VerificationToken newToken = userService.generateNewVerificationToken(existingToken);

User user = userService.getUser(newToken.getToken());
String appUrl =
"http://" + request.getServerName() +
":" + request.getServerPort() +
request.getContextPath();
SimpleMailMessage email =
constructResendVerificationTokenEmail(appUrl, request.getLocale(), newToken, user);
mailSender.send(email);

return new GenericResponse(
messages.getMessage("message.resendToken", null, request.getLocale()));
}

И утилита для фактического создания сообщения электронной почты, которое получает пользователь, —structResendVerificationTokenEmail () :

private SimpleMailMessage constructResendVerificationTokenEmail
(String contextPath, Locale locale, VerificationToken newToken, User user) {
String confirmationUrl =
contextPath + "/regitrationConfirm.html?token=" + newToken.getToken();
String message = messages.getMessage("message.resendToken", null, locale);
SimpleMailMessage email = new SimpleMailMessage();
email.setSubject("Resend Registration Token");
email.setText(message + " rn" + confirmationUrl);
email.setFrom(env.getProperty("support.email"));
email.setTo(user.getEmail());
return email;
}

Нам также необходимо изменить существующий функционал регистрации — добавив в модель новую информацию об истечении срока действия токена :

@GetMapping("/registrationConfirm")
public String confirmRegistration(
Locale locale, Model model, @RequestParam("token") String token) {
VerificationToken verificationToken = userService.getVerificationToken(token);
if (verificationToken == null) {
String message = messages.getMessage("auth.message.invalidToken", null, locale);
model.addAttribute("message", message);
return "redirect:/badUser.html?lang=" + locale.getLanguage();
}

User user = verificationToken.getUser();
Calendar cal = Calendar.getInstance();
if ((verificationToken.getExpiryDate().getTime() - cal.getTime().getTime()) <= 0) {
model.addAttribute("message", messages.getMessage("auth.message.expired", null, locale));
model.addAttribute("expired", true);
model.addAttribute("token", token);
return "redirect:/badUser.html?lang=" + locale.getLanguage();
}

user.setEnabled(true);
userService.saveRegisteredUser(user);
model.addAttribute("message", messages.getMessage("message.accountVerified", null, locale));
return "redirect:/login.html?lang=" + locale.getLanguage();
}

3. Обработчик исключений

Предыдущий функционал — при определенных условиях — генерация исключений; эти исключения необходимо обрабатывать, и мы собираемся сделать это с помощью специального обработчика исключений :

@ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

@Autowired
private MessageSource messages;

@ExceptionHandler({ UserNotFoundException.class })
public ResponseEntity<Object> handleUserNotFound(RuntimeException ex, WebRequest request) {
logger.error("404 Status Code", ex);
GenericResponse bodyOfResponse = new GenericResponse(
messages.getMessage("message.userNotFound", null, request.getLocale()), "UserNotFound");

return handleExceptionInternal(
ex, bodyOfResponse, new HttpHeaders(), HttpStatus.NOT_FOUND, request);
}

@ExceptionHandler({ MailAuthenticationException.class })
public ResponseEntity<Object> handleMail(RuntimeException ex, WebRequest request) {
logger.error("500 Status Code", ex);
GenericResponse bodyOfResponse = new GenericResponse(
messages.getMessage(
"message.email.config.error", null, request.getLocale()), "MailError");

return handleExceptionInternal(
ex, bodyOfResponse, new HttpHeaders(), HttpStatus.NOT_FOUND, request);
}

@ExceptionHandler({ Exception.class })
public ResponseEntity<Object> handleInternal(RuntimeException ex, WebRequest request) {
logger.error("500 Status Code", ex);
GenericResponse bodyOfResponse = new GenericResponse(
messages.getMessage(
"message.error", null, request.getLocale()), "InternalError");

return handleExceptionInternal(
ex, bodyOfResponse, new HttpHeaders(), HttpStatus.NOT_FOUND, request);
}
}

Обратите внимание, что:

  • мы использовали аннотацию @ControllerAdvice для обработки исключений во всем приложении.
  • мы использовали простой объект GenericResponse для отправки ответа:
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;
}
}

4. Измените файл badUser.html

Теперь мы изменим файл badUser.html , разрешив пользователю получать новый VerificationToken только в том случае, если срок действия его токена истек:

<html>
<head>
<title th:text="#{label.badUser.title}">bad user</title>
</head>
<body>
<h1 th:text="${param.message[0]}">error</h1>
<br>
<a th:href="@{/user/registration}" th:text="#{label.form.loginSignUp}">
signup</a>

<div th:if="${param.expired[0]}">
<h1 th:text="#{label.form.resendRegistrationToken}">resend</h1>
<button onclick="resendToken()"
th:text="#{label.form.resendRegistrationToken}">resend</button>

<script src="jquery.min.js"></script>
<script type="text/javascript">

var serverContext = [[@{/}]];

function resendToken(){
$.get(serverContext + "user/resendRegistrationToken?token=" + token,
function(data){
window.location.href =
serverContext +"login.html?message=" + data.message;
})
.fail(function(data) {
if(data.responseJSON.error.indexOf("MailError") > -1) {
window.location.href = serverContext + "emailError.html";
}
else {
window.location.href =
serverContext + "login.html?message=" + data.responseJSON.message;
}
});
}
</script>
</div>
</body>
</html>

Обратите внимание, что здесь мы использовали очень простой javascript и JQuery для обработки ответа «/user/resendRegistrationToken» и перенаправления пользователя на его основе.

5. Вывод

В этой быстрой статье мы разрешили пользователю запросить новую ссылку для подтверждения для активации своей учетной записи , если срок действия старой истек.

Полную реализацию этого руководства можно найти в проекте github — это проект на основе Eclipse, поэтому его легко импортировать и запускать как есть.