15
votes

I have implemented change password functionality using spring security but ((UserDetails) principal).getPassword()) is returning null for logged in user.

If I remember correctly, this used to work earlier in 3.0. Was this changed in 3.1 so that the current password of the logged in user cannot be retrieved?

In the below code I am checking the current password typed in user password from the web page. I am then checking if the logged in user's password matches the typed in password. If it does then I want to set oldPasswordMatchNewPassword = true.

How do I implement this functionality?

@RequestMapping(value = "/account/changePassword.do", method = RequestMethod.POST)
    public String submitChangePasswordPage(
            @RequestParam("oldpassword") String oldPassword,
            @RequestParam("password") String newPassword) {
        Object principal = SecurityContextHolder.getContext()
                .getAuthentication().getPrincipal();
        String username = principal.toString();
        if (principal instanceof UserDetails) {
            username = ((UserDetails) principal).getUsername();
            System.out.println("username: " + username);
            System.out.println("password: "
                    + ((UserDetails) principal).getPassword());
            if (((UserDetails) principal).getPassword() != null) {
                if (((UserDetails) principal).getPassword().equals(oldPassword)) {
                    oldPasswordMatchNewPassword = true;
                }
            }
        }
    if (oldPasswordMatchNewPassword == true) {
        logger.info("Old password matches new password. Password will be updated.");
        changePasswordDao.changePassword(username, newPassword);
        SecurityContextHolder.clearContext();
        return "redirect:home.do";
    } else {
        logger.info("Old password did not match new password. Password will be not be updated.");
        return null;
    }
}

I put a couple of sysout()s so that I can see the values returned. For ((UserDetails) principal).getUsername() I can see the correct logged in user. ((UserDetails) principal).getPassword() it is returning null.

How do I get ((UserDetails) principal).getPassword() this value?

Thanks in advance!

3

3 Answers

26
votes

I used this block of code (erase-credentials="false") to fix this. I do not know if this is an elegant solution but it fixed my problem:

<authentication-manager alias="authenticationManager" erase-credentials="false">
    <!-- authentication-provider user-service-ref="userService" -->
    <authentication-provider>
        <jdbc-user-service data-source-ref="dataSource" />
    </authentication-provider>
</authentication-manager>
6
votes

Yes, this has changed in version 3.1. Credentials are cleared after a successfull authentication by default. You can set eraseCredentialsAfterAuthentication to false on the ProviderManager to prevent this. See details here: http://static.springsource.org/spring-security/site/docs/3.2.x/reference/core-services.html#core-services-erasing-credentials

2
votes

Since the password isn't retained in memory after the user has been authenticated (generally a good thing), you would need to explicitly reload it in order to use it. An alternative and more flexible strategy is to inject an instance of the AuthenticationManager and use that directly:

String name = SecurityContextHolder.getContext().getAuthentication();

try {
    authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(name, oldPassword));
    // Update password here with your dao
} catch (AuthenticationException e) {
    // Old password was wrong
}

that way you don't need to worry about things like password-encoding strategies. Note that you shouldn't be storing passwords in plain text. They should be hashed using bcrypt or something similar.