Quantcast
Channel: Baeldung
Viewing all articles
Browse latest Browse all 3550

Spring Security – Reset Your Password

$
0
0

I usually post about Security on Twitter - you can follow me there:

1. Overview

In this tutorial – we’re continuing the ongoing Registration with Spring Security series with a look at the basic “I forgot my password” feature – so that the user can safely reset their own password when they need to.

2. The Password Reset Token

Let’s start by creating a PasswordResetToken entity to use it for resetting the users password:

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

When a password reset is triggered – a token will be created and a special link containing this token will be emailed to the user.

The token and the link will only be valid for a set period of time (24 hours in this example).

3. forgotPassword.html

The first page in the process is the “I forgot my password” page – where the user is prompted for their email address in order for the actual reset process to start.

So – let’s craft a simple forgotPassword.html asking the user for an email address:

<!DOCTYPE html>
<%@ 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"%>
<html>
<head>
    <link href="<c:url value="/resources/bootstrap.css" />" rel="stylesheet">
    <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
    <title><spring:message code="message.resetPassword"></spring:message></title>
</head>
<body>
<div class="container">
<div class="span12">
<h1>
    <spring:message code="message.resetPassword"></spring:message>
</h1>
<div>
<br>

<tr>
    <td><label><spring:message code="label.user.email"></spring:message></label></td>
    <td><input id="email" name="email" type="email" value="" /></td>
</tr>

<button type="submit" onclick="resetPass()">
    <spring:message code="message.resetPassword"></spring:message>
</button>
</div>

<br> 
<a href="<c:url value="registration.html" />">
    <spring:message code="label.form.loginSignUp"></spring:message>
</a>

<br>
<a href="<c:url value="login.html" />">
    <spring:message code="label.form.loginLink"></spring:message>
</a>
</div>
</div>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script type="text/javascript">
function resetPass(){
    var email = $("#email").val();
    $.post("<c:url value="/user/resetPassword"></c:url>",{email: email} ,function(data){
        if(data.indexOf("MailError") > -1) {
            window.location.href = "<c:url value="/emailError.html"></c:url>";
        }
        else if(data.indexOf("InternalError") > -1){
            window.location.href = 
              "<c:url value="/login.html">
                <c:param name="message" value="Error Occurred"/>
              </c:url>";
        }
        else{
            window.location.href = "<c:url value="/login.html"></c:url>" + "?message=" + data;
        }
    });
}
</script>
</body>
</html>

We’ll enable the user to reset their password by adding a link to reset password in login.html:

Current Locale : ${pageContext.response.locale} 
<br> 
<a href="<c:url value="/user/registration" />">
    <spring:message code="label.form.loginSignUp"></spring:message>
</a>
<br> 
<a href="<c:url value="/forgotPassword.html" />">
    <spring:message code="message.resetPassword"></spring:message>
</a>

4. Create the PasswordResetToken

Next, we’ll create the new PasswordResetToken and send it via email to the user:

@RequestMapping(value = "/user/resetPassword", method = RequestMethod.POST)
public @ResponseBody String resetPassword(
  HttpServletRequest request, Model model, @RequestParam("email") String userEmail) 
  throws JsonProcessingException, NoSuchMessageException {
    
    User user = userService.findUserByEmail(userEmail);
    if (user == null) {
        return new ObjectMapper().writeValueAsString(
          messages.getMessage("message.userNotFound", null, request.getLocale()));
    }

    String token = UUID.randomUUID().toString();
    userService.createPasswordResetTokenForUser(user, token);
    String appUrl = 
      "http://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
    SimpleMailMessage email = constructResetTokenEmail(appUrl, request.getLocale(), token, user);
    mailSender.send(email);

    return new ObjectMapper().writeValueAsString(
      messages.getMessage("message.resetPasswordEmail", null, request.getLocale()));
}

And here is method constructResetTokenEmail() – used to send an email with the reset token:

private SimpleMailMessage constructResetTokenEmail(
  String contextPath, Locale locale, String token, User user) {
    String url = contextPath + "/user/changePassword?id=" + user.getId() + "&token=" + token;
    String message = messages.getMessage("message.resetPassword", null, locale);
    SimpleMailMessage email = new SimpleMailMessage();
    email.setTo(user.getEmail());
    email.setSubject("Reset Password");
    email.setText(message + " \r\n" + url);
    email.setFrom(env.getProperty("support.email"));
    return email;
}

5. Process the PasswordResetToken

The user gets the email with the unique link for resetting their password, and clicks the link:

@RequestMapping(value = "/user/changePassword", method = RequestMethod.GET)
public String changePassword(
  HttpServletRequest request, Model model, @RequestParam("id") long id, @RequestParam("token") String token) {
    Locale locale = request.getLocale();

    PasswordResetToken passToken = userService.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);
        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();
}

As you can see – if the token is valid, the user will be authorized to change their password, and directed to a page to update their password.

6. Change Password

At this point, the user sees the simple Password Reset page – where the only possible option is to provide a new password:

6.1. updatePassword.html

<!DOCTYPE html>
<%@ 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"%>
<html>
<head>
<link href="<c:url value="/resources/bootstrap.css" />" rel="stylesheet">
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title><spring:message code="message.updatePassword"></spring:message></title>
</head>
<body>
<sec:authorize access="hasRole('READ_PRIVILEGE')">
    <div class="container">
        <div class="span12">
            <h1>
                <spring:message code="message.resetYourPassword"></spring:message>
            </h1>
            <form:form action="user/savePassword" method="POST" enctype="utf8">
                <br>
                <tr>
                    <td><label>
                        <spring:message code="label.user.password"></spring:message>
                    </label></td>
                    <td><input id="pass" name="password" type="password" value="" /></td>
                </tr>
                <tr>
                    <td><label>
                        <spring:message code="label.user.confirmPass"></spring:message>
                    </label></td>
                    <td>
                        <input id="passConfirm" type="password" value="" />
                        <span id="error" class="alert alert-error" style="display:none">
                             <spring:message code="PasswordMatches.user"></spring:message>
                        </span>
                    </td>
                </tr>
                <br><br>
                <button type="submit">
                    <spring:message code="message.updatePassword"></spring:message>
                </button>
            </form:form>
        </div>
    </div>
    </sec:authorize>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
      $('form').on('submit', function(e){
        var valid = $("#pass").val() == $("#passConfirm").val();
        if(!valid) {
          e.preventDefault();
          $("#error").show();
        }
      });
    });
</script>   
</body>

</html>

6.2. Save User Password

Finally, when the previous form is submitted – the new user password is saved:

@RequestMapping(value = "/user/savePassword", method = RequestMethod.POST)
@PreAuthorize("hasRole('READ_PRIVILEGE')")
public String savePassword(
  HttpServletRequest request, Model model, @RequestParam("password") String password) {
    Locale locale = request.getLocale();

    User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    userService.changeUserPassword(user, password);
    model.addAttribute("message", messages.getMessage("message.resetPasswordSuc", null, locale));
    return "redirect:/login.html?lang=" + locale;
}

7. Conclusion

In this article we implemented a simple but very useful feature for a mature Authentication process – the option to reset your own password, as a user of the system.

The full implementation of this tutorial can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.

I usually post about Security on Twitter - you can follow me there:



Viewing all articles
Browse latest Browse all 3550

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>