From cfed69220eecf7623fd779ed5d8749699615d3fe Mon Sep 17 00:00:00 2001 From: DOHA Date: Mon, 2 Feb 2015 22:46:53 +0200 Subject: [PATCH] spring security reset password --- .../dao/PasswordResetTokenRepository.java | 12 ++ .../persistence/model/PasswordResetToken.java | 133 ++++++++++++++++++ .../persistence/model/VerificationToken.java | 13 +- .../persistence/service/IUserService.java | 14 ++ .../persistence/service/UserService.java | 39 +++++ .../listener/RegistrationListener.java | 1 + .../security/MyUserDetailsService.java | 2 +- .../java/org/baeldung/spring/MvcConfig.java | 2 + .../controller/RegistrationController.java | 110 ++++++++++++++- .../src/main/resources/messages_en.properties | 7 +- .../main/resources/messages_es_ES.properties | 1 + .../src/main/resources/webSecurityConfig.xml | 6 + .../src/main/webapp/WEB-INF/view/badUser.jsp | 10 ++ .../webapp/WEB-INF/view/forgetPassword.jsp | 39 +++++ .../src/main/webapp/WEB-INF/view/login.jsp | 2 + .../webapp/WEB-INF/view/updatePassword.jsp | 41 ++++++ 16 files changed, 423 insertions(+), 9 deletions(-) create mode 100644 spring-security-login-and-registration/src/main/java/org/baeldung/persistence/dao/PasswordResetTokenRepository.java create mode 100644 spring-security-login-and-registration/src/main/java/org/baeldung/persistence/model/PasswordResetToken.java create mode 100644 spring-security-login-and-registration/src/main/webapp/WEB-INF/view/forgetPassword.jsp create mode 100644 spring-security-login-and-registration/src/main/webapp/WEB-INF/view/updatePassword.jsp diff --git a/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/dao/PasswordResetTokenRepository.java b/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/dao/PasswordResetTokenRepository.java new file mode 100644 index 0000000000..9ef80fe8b1 --- /dev/null +++ b/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/dao/PasswordResetTokenRepository.java @@ -0,0 +1,12 @@ +package org.baeldung.persistence.dao; + +import org.baeldung.persistence.model.PasswordResetToken; +import org.baeldung.persistence.model.User; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PasswordResetTokenRepository extends JpaRepository { + + public PasswordResetToken findByToken(String token); + + public PasswordResetToken findByUser(User user); +} diff --git a/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/model/PasswordResetToken.java b/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/model/PasswordResetToken.java new file mode 100644 index 0000000000..d955ff970c --- /dev/null +++ b/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/model/PasswordResetToken.java @@ -0,0 +1,133 @@ +package org.baeldung.persistence.model; + +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.Date; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; + +@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; + + public PasswordResetToken() { + super(); + } + + public PasswordResetToken(String token) { + super(); + + this.token = token; + this.expiryDate = calculateExpiryDate(EXPIRATION); + } + + public PasswordResetToken(String token, User user) { + super(); + + this.token = token; + this.user = user; + this.expiryDate = calculateExpiryDate(EXPIRATION); + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public Date getExpiryDate() { + return expiryDate; + } + + public void setExpiryDate(Date expiryDate) { + this.expiryDate = expiryDate; + } + + private Date calculateExpiryDate(int expiryTimeInMinutes) { + Calendar cal = Calendar.getInstance(); + cal.setTime(new Timestamp(cal.getTime().getTime())); + cal.add(Calendar.MINUTE, expiryTimeInMinutes); + return new Date(cal.getTime().getTime()); + } + + public void updateToken(String token) { + this.token = token; + this.expiryDate = calculateExpiryDate(EXPIRATION); + } + + // + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((expiryDate == null) ? 0 : expiryDate.hashCode()); + result = prime * result + ((token == null) ? 0 : token.hashCode()); + result = prime * result + ((user == null) ? 0 : user.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PasswordResetToken other = (PasswordResetToken) obj; + if (expiryDate == null) { + if (other.expiryDate != null) + return false; + } else if (!expiryDate.equals(other.expiryDate)) + return false; + if (token == null) { + if (other.token != null) + return false; + } else if (!token.equals(other.token)) + return false; + if (user == null) { + if (other.user != null) + return false; + } else if (!user.equals(other.user)) + return false; + return true; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("Token [String=").append(token).append("]").append("[Expires").append(expiryDate).append("]"); + return builder.toString(); + } + +} diff --git a/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/model/VerificationToken.java b/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/model/VerificationToken.java index aa5e311045..e5ffef9434 100644 --- a/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/model/VerificationToken.java +++ b/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/model/VerificationToken.java @@ -1,10 +1,8 @@ package org.baeldung.persistence.model; import java.util.Calendar; -import java.sql.Date; -import java.sql.Timestamp; +import java.util.Date; -import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; @@ -16,7 +14,7 @@ import javax.persistence.OneToOne; @Entity public class VerificationToken { - private static final int EXPIRATION = 60 * 24; + private static final int EXPIRATION = 2;// 60 * 24; @Id @GeneratedValue(strategy = GenerationType.AUTO) @@ -75,11 +73,16 @@ public class VerificationToken { private Date calculateExpiryDate(int expiryTimeInMinutes) { Calendar cal = Calendar.getInstance(); - cal.setTime(new Timestamp(cal.getTime().getTime())); + cal.setTimeInMillis(new Date().getTime()); cal.add(Calendar.MINUTE, expiryTimeInMinutes); return new Date(cal.getTime().getTime()); } + public void updateToken(String token) { + this.token = token; + this.expiryDate = calculateExpiryDate(EXPIRATION); + } + // @Override diff --git a/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/service/IUserService.java b/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/service/IUserService.java index 94cdb3a2bb..bc17a7d3b4 100644 --- a/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/service/IUserService.java +++ b/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/service/IUserService.java @@ -1,5 +1,6 @@ package org.baeldung.persistence.service; +import org.baeldung.persistence.model.PasswordResetToken; import org.baeldung.persistence.model.User; import org.baeldung.persistence.model.VerificationToken; import org.baeldung.validation.EmailExistsException; @@ -18,4 +19,17 @@ public interface IUserService { VerificationToken getVerificationToken(String VerificationToken); + VerificationToken updateVerificationToken(String token); + + void createPasswordResetTokenForUser(User user, String token); + + User findUserByEmail(String email); + + PasswordResetToken getPasswordResetToken(String token); + + User getUserByPasswordResetToken(String token); + + User getUserByID(long id); + + void changeUserPassword(User user, String password); } diff --git a/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/service/UserService.java b/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/service/UserService.java index 00e3a4a46f..8d1efdc203 100644 --- a/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/service/UserService.java +++ b/spring-security-login-and-registration/src/main/java/org/baeldung/persistence/service/UserService.java @@ -1,12 +1,15 @@ package org.baeldung.persistence.service; import java.util.Arrays; +import java.util.UUID; import javax.transaction.Transactional; +import org.baeldung.persistence.dao.PasswordResetTokenRepository; import org.baeldung.persistence.dao.RoleRepository; import org.baeldung.persistence.dao.UserRepository; import org.baeldung.persistence.dao.VerificationTokenRepository; +import org.baeldung.persistence.model.PasswordResetToken; import org.baeldung.persistence.model.User; import org.baeldung.persistence.model.VerificationToken; import org.baeldung.validation.EmailExistsException; @@ -23,6 +26,9 @@ public class UserService implements IUserService { @Autowired private VerificationTokenRepository tokenRepository; + @Autowired + private PasswordResetTokenRepository passwordTokenRepository; + @Autowired private PasswordEncoder passwordEncoder; @@ -74,6 +80,39 @@ public class UserService implements IUserService { tokenRepository.save(myToken); } + public VerificationToken updateVerificationToken(String verificationToken) { + VerificationToken vToken = tokenRepository.findByToken(verificationToken); + vToken.updateToken(UUID.randomUUID().toString()); + vToken = tokenRepository.save(vToken); + return vToken; + } + + public void createPasswordResetTokenForUser(User user, String token) { + PasswordResetToken myToken = new PasswordResetToken(token, user); + passwordTokenRepository.save(myToken); + } + + public User findUserByEmail(String email) { + return repository.findByEmail(email); + } + + public PasswordResetToken getPasswordResetToken(String token) { + return passwordTokenRepository.findByToken(token); + } + + public User getUserByPasswordResetToken(String token) { + return passwordTokenRepository.findByToken(token).getUser(); + } + + public User getUserByID(long id) { + return repository.findOne(id); + } + + public void changeUserPassword(User user, String password) { + user.setPassword(passwordEncoder.encode(password)); + repository.save(user); + } + private boolean emailExist(String email) { User user = repository.findByEmail(email); if (user != null) { diff --git a/spring-security-login-and-registration/src/main/java/org/baeldung/registration/listener/RegistrationListener.java b/spring-security-login-and-registration/src/main/java/org/baeldung/registration/listener/RegistrationListener.java index 0ec64ba408..48acab068e 100644 --- a/spring-security-login-and-registration/src/main/java/org/baeldung/registration/listener/RegistrationListener.java +++ b/spring-security-login-and-registration/src/main/java/org/baeldung/registration/listener/RegistrationListener.java @@ -41,6 +41,7 @@ public class RegistrationListener implements ApplicationListener getAuthorities(final Collection roles) { + public final Collection getAuthorities(final Collection roles) { return getGrantedAuthorities(getPrivileges(roles)); } diff --git a/spring-security-login-and-registration/src/main/java/org/baeldung/spring/MvcConfig.java b/spring-security-login-and-registration/src/main/java/org/baeldung/spring/MvcConfig.java index b6cdd986ee..91e4844be6 100644 --- a/spring-security-login-and-registration/src/main/java/org/baeldung/spring/MvcConfig.java +++ b/spring-security-login-and-registration/src/main/java/org/baeldung/spring/MvcConfig.java @@ -48,6 +48,8 @@ public class MvcConfig extends WebMvcConfigurerAdapter { registry.addViewController("/admin.html"); registry.addViewController("/registration.html"); registry.addViewController("/successRegister.html"); + registry.addViewController("/forgetPassword.html"); + registry.addViewController("/updatePassword.html"); } @Override diff --git a/spring-security-login-and-registration/src/main/java/org/baeldung/web/controller/RegistrationController.java b/spring-security-login-and-registration/src/main/java/org/baeldung/web/controller/RegistrationController.java index 3db7764c93..950b8cf214 100644 --- a/spring-security-login-and-registration/src/main/java/org/baeldung/web/controller/RegistrationController.java +++ b/spring-security-login-and-registration/src/main/java/org/baeldung/web/controller/RegistrationController.java @@ -1,14 +1,18 @@ package org.baeldung.web.controller; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; +import java.util.UUID; import javax.validation.Valid; +import org.baeldung.persistence.model.PasswordResetToken; import org.baeldung.persistence.model.User; import org.baeldung.persistence.model.VerificationToken; -import org.baeldung.persistence.service.UserDto; import org.baeldung.persistence.service.IUserService; +import org.baeldung.persistence.service.UserDto; import org.baeldung.registration.OnRegistrationCompleteEvent; import org.baeldung.validation.EmailExistsException; import org.slf4j.Logger; @@ -16,7 +20,13 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.MessageSource; +import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; @@ -45,6 +55,9 @@ public class RegistrationController { @Autowired private ApplicationEventPublisher eventPublisher; + @Autowired + private UserDetailsService userDetailsService; + public RegistrationController() { } @@ -70,8 +83,14 @@ public class RegistrationController { User user = verificationToken.getUser(); Calendar cal = Calendar.getInstance(); + DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); + System.out.println(df.format(verificationToken.getExpiryDate())); + System.out.println(df.format(cal.getTime())); + if ((verificationToken.getExpiryDate().getTime() - cal.getTime().getTime()) <= 0) { model.addAttribute("message", messages.getMessage("auth.message.expired", null, locale)); + model.addAttribute("expired", true); + model.addAttribute("token", token); return "redirect:/badUser.html?lang=" + locale.getLanguage(); } @@ -100,6 +119,93 @@ public class RegistrationController { return new ModelAndView("successRegister", "user", accountDto); } + @RequestMapping(value = "/user/resendRegistrationToken", method = RequestMethod.GET) + public String resendRegistrationToken(WebRequest request, Model model, @RequestParam("token") String token) { + Locale locale = request.getLocale(); + VerificationToken newToken = service.updateVerificationToken(token); + User user = service.getUser(newToken.getToken()); + try { + String confirmationUrl = request.getContextPath() + "/regitrationConfirm.html?token=" + newToken.getToken(); + String message = messages.getMessage("message.resendToken", null, request.getLocale()); + SimpleMailMessage email = new SimpleMailMessage(); + email.setTo(user.getEmail()); + email.setSubject("Resend Registration Token"); + email.setText(message + " \r\n" + "http://localhost:8080" + confirmationUrl); + System.out.println(email.getText()); + mailSender.send(email); + System.out.println(email.getText()); + } catch (Exception e) { + return "redirect:/emailError.html?lang=" + locale.getLanguage(); + } + model.addAttribute("message", messages.getMessage("message.resendToken", null, locale)); + return "redirect:/login.html?lang=" + locale.getLanguage(); + } + + @RequestMapping(value = "/user/resetPassword", method = RequestMethod.POST) + public String resetPassword(WebRequest request, Model model, @RequestParam("email") String userEmail) { + + User user = service.findUserByEmail(userEmail); + if (user == null) { + model.addAttribute("message", messages.getMessage("auth.message.expired", null, request.getLocale())); + return "redirect:/login.html?lang=" + request.getLocale().getLanguage(); + } + + String token = UUID.randomUUID().toString(); + service.createPasswordResetTokenForUser(user, token); + try { + String url = request.getContextPath() + "/user/changePassword?id=" + user.getId() + "&token=" + token; + String message = messages.getMessage("message.resetPassword", null, request.getLocale()); + SimpleMailMessage email = new SimpleMailMessage(); + email.setTo(user.getEmail()); + email.setSubject("Reset Password"); + email.setText(message + " \r\n" + "http://localhost:8080" + url); + System.out.println(email.getText()); + mailSender.send(email); + System.out.println(email.getText()); + } catch (Exception e) { + return "redirect:/emailError.html?lang=" + request.getLocale().getLanguage(); + } + model.addAttribute("message", messages.getMessage("message.resetPassword", null, request.getLocale())); + return "redirect:/login.html?lang=" + request.getLocale().getLanguage(); + } + + @RequestMapping(value = "/user/changePassword", method = RequestMethod.GET) + public String changePassword(WebRequest request, Model model, @RequestParam("id") long id, @RequestParam("token") String token) { + Locale locale = request.getLocale(); + + PasswordResetToken passToken = service.getPasswordResetToken(token); + User user = passToken.getUser(); + if (passToken == null || user.getId() != id) { + String message = messages.getMessage("auth.message.invalidToken", null, locale); + model.addAttribute("message", message); + System.out.println(id); + System.out.println(passToken); + return "redirect:/login.html?lang=" + locale.getLanguage(); + } + + Calendar cal = Calendar.getInstance(); + if ((passToken.getExpiryDate().getTime() - cal.getTime().getTime()) <= 0) { + model.addAttribute("message", messages.getMessage("auth.message.expired", null, locale)); + return "redirect:/login.html?lang=" + locale.getLanguage(); + } + + Authentication auth = new UsernamePasswordAuthenticationToken(user, null, userDetailsService.loadUserByUsername(user.getEmail()).getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(auth); + + return "redirect:/updatePassword.html?lang=" + locale.getLanguage(); + } + + @RequestMapping(value = "/user/savePassword", method = RequestMethod.POST) + @PreAuthorize("hasRole('READ_PRIVILEGE')") + public String savePassword(WebRequest request, Model model, @RequestParam("password") String password) { + Locale locale = request.getLocale(); + + User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + service.changeUserPassword(user, password); + + return "redirect:/login.html?lang=" + locale; + } + private User createUserAccount(UserDto accountDto) { User registered = null; try { @@ -109,4 +215,4 @@ public class RegistrationController { } return registered; } -} +} \ No newline at end of file diff --git a/spring-security-login-and-registration/src/main/resources/messages_en.properties b/spring-security-login-and-registration/src/main/resources/messages_en.properties index 1a66bbc0f4..123ec2fabd 100644 --- a/spring-security-login-and-registration/src/main/resources/messages_en.properties +++ b/spring-security-login-and-registration/src/main/resources/messages_en.properties @@ -52,4 +52,9 @@ NotEmpty.user.password=Password required NotNull.user.matchingPassword=Required NotEmpty.user.matchingPassword=Required PasswordMatches.user:Password does not match! -Email.user.email=Invalid Username (Email) \ No newline at end of file +Email.user.email=Invalid Username (Email) +label.form.resendRegistrationToken=Re-send Token +message.resendToken=We will send you a message with a new registration token to your email account. +message.forgetPassword=Forget Password +message.resetPassword=Reset Password +message.updatePassword=Update Password \ No newline at end of file diff --git a/spring-security-login-and-registration/src/main/resources/messages_es_ES.properties b/spring-security-login-and-registration/src/main/resources/messages_es_ES.properties index a22263b9ba..54ed8f4edb 100644 --- a/spring-security-login-and-registration/src/main/resources/messages_es_ES.properties +++ b/spring-security-login-and-registration/src/main/resources/messages_es_ES.properties @@ -53,3 +53,4 @@ NotNull.user.matchingPassword=Campo obligatirio NotEmpty.user.matchingPassword=Campo obligatrio PasswordMatches.user:Las claves no coinciden! Email.user.email=Email no es valido +label.form.resendRegistrationToken=Reenviar mensaje de emergencia \ No newline at end of file diff --git a/spring-security-login-and-registration/src/main/resources/webSecurityConfig.xml b/spring-security-login-and-registration/src/main/resources/webSecurityConfig.xml index 2065dda218..b929f1ffa9 100644 --- a/spring-security-login-and-registration/src/main/resources/webSecurityConfig.xml +++ b/spring-security-login-and-registration/src/main/resources/webSecurityConfig.xml @@ -15,6 +15,12 @@ + + + + + + diff --git a/spring-security-login-and-registration/src/main/webapp/WEB-INF/view/badUser.jsp b/spring-security-login-and-registration/src/main/webapp/WEB-INF/view/badUser.jsp index 348caac58a..ecde61988f 100644 --- a/spring-security-login-and-registration/src/main/webapp/WEB-INF/view/badUser.jsp +++ b/spring-security-login-and-registration/src/main/webapp/WEB-INF/view/badUser.jsp @@ -20,5 +20,15 @@ code="label.badUser.title"> "> + +
+

${label.form.resendRegistrationToken}

+ + + "> + + +
+ diff --git a/spring-security-login-and-registration/src/main/webapp/WEB-INF/view/forgetPassword.jsp b/spring-security-login-and-registration/src/main/webapp/WEB-INF/view/forgetPassword.jsp new file mode 100644 index 0000000000..21e394710f --- /dev/null +++ b/spring-security-login-and-registration/src/main/webapp/WEB-INF/view/forgetPassword.jsp @@ -0,0 +1,39 @@ + +<%@ page contentType="text/html;charset=UTF-8" language="java"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> +<%@ taglib prefix="sec" + uri="http://www.springframework.org/security/tags"%> +<%@ page session="false"%> + + +" rel="stylesheet"> + +<spring:message code="message.resetPassword"></spring:message> + + +
+
+

+ +

+ +
+ + + + + + + +
+
"> +
+
+ + + \ No newline at end of file diff --git a/spring-security-login-and-registration/src/main/webapp/WEB-INF/view/login.jsp b/spring-security-login-and-registration/src/main/webapp/WEB-INF/view/login.jsp index 7ffa78c777..dbcef6b9ea 100644 --- a/spring-security-login-and-registration/src/main/webapp/WEB-INF/view/login.jsp +++ b/spring-security-login-and-registration/src/main/webapp/WEB-INF/view/login.jsp @@ -25,6 +25,8 @@
+ ">
diff --git a/spring-security-login-and-registration/src/main/webapp/WEB-INF/view/updatePassword.jsp b/spring-security-login-and-registration/src/main/webapp/WEB-INF/view/updatePassword.jsp new file mode 100644 index 0000000000..a29672bdbf --- /dev/null +++ b/spring-security-login-and-registration/src/main/webapp/WEB-INF/view/updatePassword.jsp @@ -0,0 +1,41 @@ + +<%@ page contentType="text/html;charset=UTF-8" language="java"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> +<%@ taglib prefix="sec" + uri="http://www.springframework.org/security/tags"%> +<%@ page session="false"%> + + +" rel="stylesheet"> + +<spring:message code="message.updatePassword"></spring:message> + + + +
+
+

+ +

+ +
+ + + + + + + +
+ +
+
+
+ + + + \ No newline at end of file