Spring Security - Tetapkan Semula Kata Laluan Anda

Artikel ini adalah sebahagian daripada siri: • Tutorial Pendaftaran Keselamatan Musim Semi

• Proses Pendaftaran Dengan Keselamatan Musim Semi

• Pendaftaran - Aktifkan Akaun Baru melalui E-mel

• Pendaftaran Keselamatan Musim Semi - Hantar semula E-mel Pengesahan

• Pendaftaran dengan Spring Security - Pengekodan Kata Laluan

• API Pendaftaran menjadi RESTful

• Keselamatan Musim Semi - Tetapkan Semula Kata Laluan Anda (artikel semasa) • Pendaftaran - Kekuatan dan Peraturan Kata Laluan

• Mengemas kini Kata Laluan anda

1. Gambaran keseluruhan

Dalam tutorial ini - kami terus berterusan Pendaftaran dengan Spring Keselamatan siri dengan melihat pada asas " Saya terlupa kata laluan saya " ciri - supaya pengguna dengan selamat boleh menetapkan semula kata laluan mereka sendiri apabila mereka perlu.

2. Minta Tetapkan Semula Kata Laluan Anda

Aliran tetapan semula kata laluan biasanya bermula apabila pengguna mengklik semacam "tetapkan semula" butang di halaman Masuk. Kemudian, kami boleh meminta pengguna untuk alamat e-mel mereka atau maklumat pengenalan lain. Setelah disahkan, kami dapat menghasilkan token dan menghantar e-mel kepada pengguna.

Gambar rajah berikut menggambarkan aliran yang akan kita laksanakan dalam artikel ini:

3. Token Tetapan Semula Kata Laluan

Mari mulakan dengan membuat entiti PasswordResetToken untuk menggunakannya untuk menetapkan semula kata laluan pengguna:

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

Apabila tetapan semula kata laluan dicetuskan - token akan dibuat dan pautan khas yang mengandungi token ini akan dihantar melalui e-mel kepada pengguna .

Token dan pautan hanya akan sah untuk jangka masa yang ditetapkan (24 jam dalam contoh ini).

4. lupaPassword.html

Halaman pertama dalam proses ini adalah halaman " Saya lupa kata laluan saya " - di mana pengguna diminta untuk alamat e-mel mereka agar proses penyetelan semula yang sebenarnya dapat dimulakan.

Oleh itu - mari kita buat Password.html yang mudah dilupakan meminta pengguna untuk alamat e-mel:

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

Kita sekarang perlu memaut ke halaman " reset password " baru ini dari halaman login:

reset

5. Buat PasswordResetToken

Mari mulakan dengan membuat PasswordResetToken baru dan hantarkan melalui e-mel kepada pengguna:

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

Dan inilah kaedah createPasswordResetTokenForUser () :

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

Dan inilah kaedah buildResetTokenEmail () - digunakan untuk menghantar e-mel dengan token tetapan semula:

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

Perhatikan bagaimana kami menggunakan objek sederhana GenericResponse untuk mewakili tindak balas kami kepada pelanggan:

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. Periksa PasswordResetToken

Apabila pengguna klik pada pautan dalam e-mel mereka, pengguna / Changepassword titik akhir:

  • mengesahkan bahawa token itu sah dan
  • membentangkan pengguna dengan halaman updatePassword , di mana dia boleh memasukkan kata laluan baru

Kata laluan dan token baru kemudian dihantar ke titik akhir pengguna / savePassword :

Pengguna mendapat e-mel dengan pautan unik untuk menetapkan semula kata laluan mereka, dan mengklik pautan:

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

Dan inilah kaedah 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. Tukar Kata Laluan

Pada ketika ini, pengguna melihat halaman Reset Kata Laluan yang mudah - di mana satu-satunya pilihan yang mungkin adalah memberikan kata laluan baru :

7.1. kemas kiniPassword.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+"

"); }); } }); }

Perhatikan bahawa kami menunjukkan token tetapan semula dan lulus sebagai parameter POST dalam panggilan berikut untuk menyimpan kata laluan.

7.2. Simpan Kata Laluan

Akhirnya, apabila permintaan pos sebelumnya dihantar - kata laluan pengguna baru disimpan:

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

Dan inilah kaedah changeUserPassword () :

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

Dan Kata Laluan Ke :

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

8. Kesimpulannya

Dalam artikel ini, kami menerapkan ciri mudah tetapi sangat berguna untuk proses Pengesahan dewasa - pilihan untuk menetapkan semula kata laluan anda sendiri, sebagai pengguna sistem.

The pelaksanaan penuh tutorial ini boleh didapati dalam projek GitHub - ini adalah projek berasaskan Eclipse, jadi ia harus mudah untuk import dan berjalan kerana ia adalah.

Seterusnya » Pendaftaran - Kekuatan dan Peraturan Kata Laluan « Sebelumnya API Pendaftaran menjadi RESTful