Spring Security - Setzen Sie Ihr Passwort zurück

Dieser Artikel ist Teil einer Reihe: • Spring Security Registration Tutorial

• Der Registrierungsprozess mit Spring Security

• Registrierung - Aktivieren Sie ein neues Konto per E-Mail

• Spring Security Registration - Bestätigungs-E-Mail erneut senden

• Registrierung bei Spring Security - Passwortkodierung

• Die Registrierungs-API wird RESTful

• Spring Security - Setzen Sie Ihr Passwort zurück (aktueller Artikel). • Registrierung - Passwortstärke und -regeln

• Aktualisieren Sie Ihr Passwort

1. Übersicht

In diesem Tutorial setzen wir die laufende Registrierung bei Spring Security fort , wobei wir uns die grundlegende Funktion " Ich habe mein Passwort vergessen " ansehen , damit der Benutzer sein eigenes Passwort bei Bedarf sicher zurücksetzen kann.

2. Fordern Sie das Zurücksetzen Ihres Passworts an

Ein Ablauf zum Zurücksetzen des Kennworts beginnt normalerweise, wenn der Benutzer auf der Anmeldeseite auf eine Art Schaltfläche zum Zurücksetzen klickt. Anschließend können wir den Benutzer nach seiner E-Mail-Adresse oder anderen identifizierenden Informationen fragen. Nach der Bestätigung können wir ein Token generieren und eine E-Mail an den Benutzer senden.

Das folgende Diagramm zeigt den Ablauf, den wir in diesem Artikel implementieren werden:

3. Das Passwort-Reset-Token

Beginnen wir mit der Erstellung einer PasswordResetToken- Entität, um das Kennwort des Benutzers zurückzusetzen:

@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; }

Wenn ein Zurücksetzen des Passworts ausgelöst wird, wird ein Token erstellt und ein spezieller Link, der dieses Token enthält, wird per E-Mail an den Benutzer gesendet .

Das Token und der Link sind nur für einen festgelegten Zeitraum gültig (in diesem Beispiel 24 Stunden).

4. vergaßPassword.html

Die erste Seite des Vorgangs ist die Seite " Ich habe mein Passwort vergessen ", auf der der Benutzer zur Eingabe seiner E-Mail-Adresse aufgefordert wird, damit der eigentliche Rücksetzvorgang gestartet werden kann.

Erstellen wir also eine einfache vergessenePassword.html, in der der Benutzer nach einer E-Mail-Adresse gefragt wird:

reset

email reset registration login var serverContext = [[@{/}]]; function resetPass(){ var email = $("#email").val(); $.post(serverContext + "user/resetPassword",{email: email} , function(data){ window.location.href = serverContext + "login?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?message=" + data.responseJSON.message; } }); }

Wir müssen jetzt von der Anmeldeseite aus auf diese neue Seite zum Zurücksetzen des Passworts verlinken :

reset

5. Erstellen Sie das PasswordResetToken

Beginnen wir mit der Erstellung des neuen PasswordResetToken und senden es per E-Mail an den Benutzer:

@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())); }

Und hier ist die Methode createPasswordResetTokenForUser () :

public void createPasswordResetTokenForUser(User user, String token) { PasswordResetToken myToken = new PasswordResetToken(token, user); passwordTokenRepository.save(myToken); }

Und hier ist die Methode constructResetTokenEmail () - zum Senden einer E-Mail mit dem Reset-Token:

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; }

Beachten Sie, wie wir ein einfaches Objekt GenericResponse verwendet haben , um unsere Antwort an den Client darzustellen :

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. Überprüfen Sie das PasswordResetToken

Sobald der Benutzer auf den Link in seiner E-Mail klickt, wird der Endpunkt user / changePassword :

  • überprüft, ob das Token gültig ist und
  • präsentiert dem Benutzer die Seite updatePassword , auf der er ein neues Passwort eingeben kann

Das neue Kennwort und das Token werden dann an den Endpunkt user / savePassword übergeben :

Der Benutzer erhält die E-Mail mit dem eindeutigen Link zum Zurücksetzen seines Passworts und klickt auf den Link:

@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(); } }

Und hier ist die validatePasswordResetToken () -Methode:

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. Ändern Sie das Passwort

At this point, the user sees the simple Password Reset page – where the only possible option is to provide a new password:

7.1. updatePassword.html

reset

password confirm token error submit var serverContext = [[@{/}]]; $(document).ready(function () { $('form').submit(function(event) { savePass(event); }); $(":password").keyup(function(){ if($("#password").val() != $("#matchPassword").val()){ $("#globalError").show().html(/*[[#{PasswordMatches.user}]]*/); }else{ $("#globalError").html("").hide(); } }); }); function savePass(event){ event.preventDefault(); if($("#password").val() != $("#matchPassword").val()){ $("#globalError").show().html(/*[[#{PasswordMatches.user}]]*/); return; } var formData= $('form').serialize(); $.post(serverContext + "user/savePassword",formData ,function(data){ window.location.href = serverContext + "login?message="+data.message; }) .fail(function(data) { if(data.responseJSON.error.indexOf("InternalError") > -1){ window.location.href = serverContext + "login?message=" + data.responseJSON.message; } else{ var errors = $.parseJSON(data.responseJSON.message); $.each( errors, function( index,item ){ $("#globalError").show().html(item.defaultMessage); }); errors = $.parseJSON(data.responseJSON.error); $.each( errors, function( index,item ){ $("#globalError").show().append(item.defaultMessage+"

"); }); } }); }

Note that we show the reset token and pass it as a POST parameter in the following call to save the password.

7.2. Save the Password

Finally, when the previous post request is submitted – the new user password is saved:

@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)); } }

And here is the changeUserPassword() method:

public void changeUserPassword(User user, String password) { user.setPassword(passwordEncoder.encode(password)); repository.save(user); }

And the PasswordDto:

public class PasswordDto { private String oldPassword; private String token; @ValidPassword private String newPassword; } 

8. Conclusion

In this article, we implemented a simple but very useful feature for a mature Authentication process – the option to reset your own password, as a user of the system.

Die vollständige Implementierung dieses Tutorials finden Sie im GitHub-Projekt - dies ist ein Eclipse-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.

Weiter » Registrierung - Kennwortstärke und Regeln « Zurück Die Registrierungs-API wird RESTful