Spring Security - Reset uw wachtwoord

Dit artikel maakt deel uit van een reeks: • Tutorial Spring Security Registration

• Het registratieproces met Spring Security

• Registratie - Activeer een nieuw account via e-mail

• Spring Security Registration - Verzend de verificatie-e-mail opnieuw

• Registratie met Spring Security - Wachtwoordcodering

• De registratie-API wordt RESTful

• Spring Security - Reset uw wachtwoord (huidig ​​artikel) • Registratie - Wachtwoordsterkte en regels

• Uw wachtwoord bijwerken

1. Overzicht

In deze tutorial gaan we verder met de voortgang Registratie bij Spring Security serie met een blik op de basis "Ik ben mijn wachtwoord vergeten" voorzien zijn van - zodat de gebruiker zijn eigen wachtwoord veilig kan resetten wanneer dat nodig is.

2. Verzoek om uw wachtwoord opnieuw in te stellen

Een proces voor het opnieuw instellen van het wachtwoord begint meestal wanneer de gebruiker op een soort van "reset" -knop op de aanmeldingspagina klikt. Vervolgens kunnen we de gebruiker om zijn e-mailadres of andere identificerende informatie vragen. Na bevestiging kunnen we een token genereren en een e-mail naar de gebruiker sturen.

Het volgende diagram visualiseert de stroom die we in dit artikel zullen implementeren:

3. Het wachtwoordhersteltoken

Laten we beginnen met het maken van een WachtwoordResetToken entiteit om het te gebruiken om het wachtwoord van de gebruiker opnieuw in te stellen:

@ Entity openbare klasse PasswordResetToken {privé statische laatste int EXPIRATION = 60 * 24; @Id @GeneratedValue (strategie = GenerationType.AUTO) privé Lange id; privé String-token; @OneToOne (targetEntity = User.class, fetch = FetchType.EAGER) @JoinColumn (nullable = false, name = "user_id") privégebruiker; privé Datum expiratiedatum; }

Wanneer een wachtwoordreset wordt geactiveerd, wordt een token gemaakt en een speciale link met dit token zal naar de gebruiker worden gemaild.

Het token en de link zijn slechts gedurende een bepaalde tijd geldig (in dit voorbeeld 24 uur).

4. vergetenPassword.html

De eerste pagina in het proces is de "Ik ben mijn wachtwoord vergeten" bladzijde - waar de gebruiker om zijn e-mailadres wordt gevraagd om het daadwerkelijke resetproces te starten.

Dus laten we een simpele maken vergetenPassword.html de gebruiker om een ​​e-mailadres vragen:

resetten

email reset registratie login var serverContext = [[@ {/}]]; functie resetPass () {var email = $ ("# email"). val (); $ .post (serverContext + "user / resetPassword", {email: email}, functie (data) {window.location.href = serverContext + "login? message =" + data.message;}) .fail (functie (data ) {if (data.responseJSON.error.indexOf ("MailError")> -1) {window.location.href = serverContext + "emailError.html";} anders {window.location.href = serverContext + "login? bericht = "+ data.responseJSON.message;}}); }

We moeten nu linken naar deze nieuwe "wachtwoord opnieuw instellen”Pagina van de inlogpagina:

resetten

5. Maak het WachtwoordResetToken

Laten we beginnen met het maken van het nieuwe WachtwoordResetToken en stuur het via e-mail naar de gebruiker:

@PostMapping ("/ user / resetPassword") openbaar GenericResponse resetPassword (verzoek HttpServletRequest, @RequestParam ("email") String userEmail) {User user = userService.findUserByEmail (userEmail); if (user == null) {gooi nieuwe UserNotFoundException (); } String-token = UUID.randomUUID (). ToString (); userService.createPasswordResetTokenForUser (gebruiker, token); mailSender.send (constructResetTokenEmail (getAppUrl (verzoek), request.getLocale (), token, gebruiker)); retourneer nieuwe GenericResponse (messages.getMessage ("message.resetPasswordEmail", null, request.getLocale ())); }

En hier is de createPasswordResetTokenForUser () methode:

openbare ongeldige createPasswordResetTokenForUser (gebruiker, tekenreeks-token) {PasswordResetToken myToken = nieuw PasswordResetToken (token, gebruiker); passwordTokenRepository.save (myToken); }

En hier is de methode constructResetTokenEmail () - gebruikt om een ​​e-mail te sturen met het reset-token:

private SimpleMailMessage constructResetTokenEmail (String contextPath, Locale locale, String token, gebruiker gebruiker) {String url = contextPath + "/ user / changePassword? token =" + token; String message = messages.getMessage ("message.resetPassword", null, locale); return constructEmail ("Reset wachtwoord", bericht + "\ r \ n" + url, gebruiker); } private SimpleMailMessage constructEmail (String onderwerp, String body, gebruiker gebruiker) {SimpleMailMessage email = nieuwe SimpleMailMessage (); email.setSubject (onderwerp); email.setText (body); email.setTo (user.getEmail ()); email.setFrom (env.getProperty ("support.email")); retour e-mail; }

Merk op hoe we een eenvoudig object hebben gebruikt GenericResponse om ons antwoord aan de klant te vertegenwoordigen:

openbare klasse GenericResponse {privé String-bericht; private String-fout; openbare GenericResponse (String-bericht) {super (); this.message = bericht; } public GenericResponse (String-bericht, String-fout) {super (); this.message = bericht; this.error = fout; }}

6. Controleer het WachtwoordResetToken

Zodra de gebruiker op de link in zijn e-mail klikt, wordt het gebruiker / changePassword eindpunt:

  • controleert of het token geldig is en
  • presenteert de gebruiker de vernieuw wachtwoord pagina, waar hij een nieuw wachtwoord kan invoeren

Het nieuwe wachtwoord en het token worden vervolgens doorgegeven aan het gebruiker / savePassword eindpunt:

De gebruiker krijgt de e-mail met de unieke link om zijn wachtwoord opnieuw in te stellen en klikt op de link:

@GetMapping ("/ user / changePassword") public String showChangePasswordPage (Landinstelling, modelmodel, @RequestParam ("token") String-token) {String-resultaat = securityService.validatePasswordResetToken (token); if (resultaat! = null) {String message = messages.getMessage ("auth.message." + resultaat, null, locale); retourneer "redirect: /login.html? lang =" + locale.getLanguage () + "& message =" + bericht; } else {model.addAttribute ("token", token); retourneer "redirect: /updatePassword.html? lang =" + locale.getLanguage (); }}

En hier is de validatePasswordResetToken () methode:

openbare String validatePasswordResetToken (String-token) {laatste PasswordResetToken passToken = passwordTokenRepository.findByToken (token); return! isTokenFound (passToken)? "invalidToken": isTokenExpired (passToken)? "verlopen": null; } private boolean isTokenFound (PasswordResetToken passToken) {return passToken! = null; } private boolean isTokenExpired (PasswordResetToken passToken) {final Calendar cal = Calendar.getInstance (); retourneer passToken.getExpiryDate (). vóór (cal.getTime ()); }

7. Wijzig het wachtwoord

Op dit punt ziet de gebruiker het simpele Wachtwoord reset pagina - waar de enige mogelijke optie is om geef een nieuw wachtwoord op:

7.1. updatePassword.html

resetten

wachtwoord bevestigingstoken fout indienen var serverContext = [[@ {/}]]; $ (document) .ready (function () {$ ('form'). submit (function (event) {savePass (event);}); $ (": wachtwoord"). keyup (function () {if ($ ("#password"). val ()! = $ ("# matchPassword"). val ()) {$ ("# globalError"). show (). html (/ * [[# {PasswordMatches.user}] ] * /);} else {$ ("# globalError"). html (""). hide ();}});}); functie savePass (event) {event.preventDefault (); if ($ ("# wachtwoord"). val ()! = $ ("# matchPassword"). val ()) {$ ("# globalError"). show (). html (/ * [[# {PasswordMatches. gebruiker}]]*/); terugkeren; } 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;} anders {var errors = $ .parseJSON (data. responseJSON.message); $ .each (fouten, functie (index, item) {$ ("# globalError"). show (). html (item.defaultMessage);}); errors = $ .parseJSON (data.responseJSON. error); $ .each (fouten, functie (index, item) {$ ("# globalError"). show (). append (item.defaultMessage + "

"); }); } }); }

Merk op dat we het reset-token tonen en het doorgeven als een POST-parameter in de volgende aanroep om het wachtwoord op te slaan.

7.2. Bewaar het wachtwoord

Ten slotte, wanneer het vorige postverzoek is ingediend, wordt het nieuwe gebruikerswachtwoord opgeslagen:

@PostMapping ("/ user / savePassword") public GenericResponse savePassword (laatste locale locale, @Valid PasswordDto passwordDto) {String resultaat = securityUserService.validatePasswordResetToken (passwordDto.getToken ()); if (resultaat! = null) {retourneer nieuwe GenericResponse (messages.getMessage ("auth.message." + resultaat, null, locale)); } Optioneel gebruiker = userService.getUserByPasswordResetToken (passwordDto.getToken ()); if (user.isPresent ()) {userService.changeUserPassword (user.get (), passwordDto.getNewPassword ()); retourneer nieuwe GenericResponse (messages.getMessage ("message.resetPasswordSuc", null, locale)); } else {retourneer nieuwe GenericResponse (messages.getMessage ("auth.message.invalid", null, locale)); }}

En hier is de changeUserPassword () methode:

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

En de WachtwoordDto:

openbare klasse PasswordDto {private String oldPassword; privé String-token; @ValidPassword private String newPassword; } 

8. Conclusie

In dit artikel hebben we een eenvoudige maar zeer nuttige functie geïmplementeerd voor een volwassen authenticatieproces: de optie om uw eigen wachtwoord opnieuw in te stellen als gebruiker van het systeem.

De volledige implementatie van deze tutorial is te vinden in het GitHub-project - dit is een op Eclipse gebaseerd project, dus het zou gemakkelijk te importeren en uit te voeren moeten zijn zoals het is.

De volgende » Registratie - Wachtwoordsterkte en regels « Vorige De registratie-API wordt RESTful