Keamanan Musim Semi - Atur Ulang Kata Sandi Anda

Artikel ini adalah bagian dari serial: • Tutorial Pendaftaran Keamanan Musim Semi

• Proses Pendaftaran Dengan Keamanan Musim Semi

• Pendaftaran - Aktifkan Akun Baru melalui Email

• Pendaftaran Keamanan Musim Semi - Kirim Ulang Email Verifikasi

• Pendaftaran dengan Spring Security - Encoding Sandi

• API Pendaftaran menjadi RESTful

• Keamanan Musim Semi - Atur Ulang Kata Sandi Anda (artikel saat ini) • Pendaftaran - Kekuatan dan Aturan Kata Sandi

• Memperbarui Kata Sandi Anda

1. Ikhtisar

Dalam tutorial ini - kami terus berlangsungnya Pendaftaran dengan Spring Security seri dengan melihat dasar “ Saya lupa password ” fitur - sehingga pengguna dapat dengan aman mereset password mereka sendiri ketika mereka perlu.

2. Minta Penyetelan Ulang Kata Sandi Anda

Alur penyetelan ulang sandi biasanya dimulai saat pengguna mengklik semacam tombol "setel ulang" di halaman Login. Kemudian, kami dapat menanyakan alamat email atau informasi identitas lainnya kepada pengguna. Setelah dikonfirmasi, kami dapat membuat token dan mengirim email ke pengguna.

Diagram berikut memvisualisasikan aliran yang akan kami terapkan dalam artikel ini:

3. Token Reset Kata Sandi

Mari kita mulai dengan membuat entitas PasswordResetToken untuk digunakan untuk menyetel ulang kata sandi 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; }

Saat penyetelan ulang sandi dipicu - token akan dibuat dan tautan khusus yang berisi token ini akan dikirim ke email pengguna .

Token dan tautan hanya akan valid untuk jangka waktu tertentu (dalam contoh ini 24 jam).

4. lupaPassword.html

Halaman pertama dalam proses ini adalah halaman " Saya lupa kata sandi " - di mana pengguna diminta memasukkan alamat email mereka untuk memulai proses penyetelan ulang yang sebenarnya.

Jadi - mari kita buat forgetPassword.html sederhana yang meminta alamat email pengguna:

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

Sekarang kita perlu menautkan ke halaman " setel ulang sandi " baru ini dari halaman login:

reset

5. Buat PasswordResetToken

Mari kita mulai dengan membuat PasswordResetToken baru dan mengirimkannya melalui email ke 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 berikut adalah metode createPasswordResetTokenForUser () :

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

Dan berikut adalah metode constructResetTokenEmail () - digunakan untuk mengirim email dengan token reset:

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 GenericResponse sederhana untuk merepresentasikan respons kami kepada klien:

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

Setelah pengguna mengklik link di email mereka, titik akhir user / changePassword :

  • memverifikasi bahwa token tersebut valid dan
  • menyajikan halaman updatePassword kepada pengguna , di mana dia bisa memasukkan kata sandi baru

Kata sandi baru dan token kemudian diteruskan ke titik akhir user / savePassword :

Pengguna mendapatkan email dengan tautan unik untuk menyetel ulang sandi mereka, dan mengeklik tautan:

@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 berikut adalah metode 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. Ubah Kata Sandi

Pada titik ini, pengguna melihat halaman Atur Ulang Kata Sandi sederhana - di mana satu-satunya pilihan yang mungkin adalah memberikan kata sandi baru :

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+"

"); }); } }); }

Perhatikan bahwa kami menunjukkan token reset dan meneruskannya sebagai parameter POST dalam panggilan berikut untuk menyimpan kata sandi.

7.2. Simpan Kata Sandi

Akhirnya, ketika permintaan posting sebelumnya dikirim - kata sandi 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 metode changeUserPassword () :

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

Dan PasswordDto :

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

8. Kesimpulan

Dalam artikel ini, kami menerapkan fitur sederhana namun sangat berguna untuk proses Autentikasi yang matang - opsi untuk menyetel ulang sandi Anda sendiri, sebagai pengguna sistem.

The implementasi penuh dari tutorial ini dapat ditemukan dalam proyek GitHub - ini adalah proyek berbasis Eclipse, sehingga harus mudah untuk impor dan berjalan seperti itu.

Berikutnya » Pendaftaran - Kekuatan dan Aturan Kata Sandi « Sebelumnya API Pendaftaran menjadi RESTful