Spring Security - Reimposta la tua password

Questo articolo fa parte di una serie: • Esercitazione sulla registrazione di Spring Security

• Il processo di registrazione con Spring Security

• Registrazione: attiva un nuovo account tramite e-mail

• Registrazione Spring Security - Invia di nuovo l'e-mail di verifica

• Registrazione con Spring Security - Codifica password

• L'API di registrazione diventa RESTful

• Spring Security - Reimposta la tua password (articolo attuale) • Registrazione - Forza e regole della password

• Aggiornamento della password

1. Panoramica

In questo tutorial, stiamo continuando la registrazione in corso con la serie Spring Security con uno sguardo alla funzione di base " Ho dimenticato la password ", in modo che l'utente possa reimpostare in sicurezza la propria password quando necessario.

2. Richiedi il ripristino della tua password

Un flusso di reimpostazione della password inizia in genere quando l'utente fa clic su una sorta di pulsante di "ripristino" nella pagina di accesso. Quindi, possiamo chiedere all'utente il suo indirizzo e-mail o altre informazioni di identificazione. Una volta confermato, possiamo generare un token e inviare un'e-mail all'utente.

Il diagramma seguente mostra il flusso che implementeremo in questo articolo:

3. Il token di reimpostazione della password

Iniziamo creando un'entità PasswordResetToken da utilizzare per reimpostare la password dell'utente:

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

Quando viene attivato un ripristino della password, verrà creato un token e un collegamento speciale contenente questo token verrà inviato tramite posta elettronica all'utente .

Il token e il collegamento saranno validi solo per un determinato periodo di tempo (24 ore in questo esempio).

4. forgotPassword.html

La prima pagina del processo è la pagina " Ho dimenticato la password " , in cui all'utente viene richiesto il proprio indirizzo e-mail per avviare l'effettivo processo di ripristino.

Quindi, creiamo un semplice forgotPassword.html che chiede all'utente un indirizzo email:

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

Ora dobbiamo collegarci a questa nuova pagina di " reimpostazione della password " dalla pagina di accesso:

reset

5. Creare il PasswordResetToken

Cominciamo creando il nuovo PasswordResetToken e inviandolo via email all'utente:

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

Ed ecco il metodo createPasswordResetTokenForUser () :

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

Ed ecco il metodo constructResetTokenEmail () - utilizzato per inviare un'e-mail con il token di ripristino:

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

Nota come abbiamo utilizzato un semplice oggetto GenericResponse per rappresentare la nostra risposta al cliente:

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. Controllare il PasswordResetToken

Una volta che l'utente fa clic sul collegamento nella propria e-mail, l' endpoint utente / changePassword :

  • verifica che il token sia valido e
  • presenta all'utente la pagina updatePassword , dove può inserire una nuova password

La nuova password e il token vengono quindi passati all'endpoint user / savePassword :

L'utente riceve l'e-mail con il collegamento univoco per reimpostare la password e fa clic sul collegamento:

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

Ed ecco il metodo validatePasswordResetToken () :

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. Modificare la password

A questo punto, l'utente vede la semplice pagina di reimpostazione della password , dove l'unica opzione possibile è fornire una nuova 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+"

"); }); } }); }

Si noti che mostriamo il token di ripristino e lo passiamo come parametro POST nella chiamata seguente per salvare la password.

7.2. Salva la password

Infine, quando viene inviata la richiesta di post precedente, la nuova password utente viene salvata:

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

Ed ecco il metodo changeUserPassword () :

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

E il PasswordDto :

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

8. Conclusione

In questo articolo, abbiamo implementato una funzione semplice ma molto utile per un processo di autenticazione maturo: l'opzione per reimpostare la tua password, come utente del sistema.

L' implementazione completa di questo tutorial può essere trovata nel progetto GitHub: questo è un progetto basato su Eclipse, quindi dovrebbe essere facile da importare ed eseguire così com'è.

Avanti » Registrazione - Forza della password e regole « Precedente L'API di registrazione diventa RESTful