Last Modified 2015.9.10

Spring Security - Password Encoder

Add Password Encoder

security.xml
<authentication-manager>
  <authentication-provider>
    <jdbc-user-service data-source-ref="dataSource"
      users-by-username-query="select 
          email as username,passwd as password,1 as enabled
        from member 
        where email = ?"
      authorities-by-username-query="select 
          email as username,authority
        from authorities 
        where email = ?" />
    <password-encoder hash="bcrypt" />
  </authentication-provider>
</authentication-manager>

If set to hash="bcrypt", the BCryptPasswordEncoder is set using the bcrypt algorithm in the implementation of interface PasswordEncoder.
If you rerun Tomcat and try to log in to johndoe@gmail.org/1111, login will fail.
This is because 1111, which is not encrypted, is stored in the member table.
Delete all data of authorities and membership tables.

sqlplus java/school

delete from authorities;

delete from member;

commit;

Join the membership.
Enter johndoe@gmail.org at e-mail address and 1111 for password.
If you try to login after signing up for membership, login will fail.
This is because the password is still stored in the member table as simple text 1111.
You need to modify UserServiceImpl.java to encrypt and store passwords.

UserServiceImpl.java

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Service
public class UserServiceImpl implements UserService {

	@Autowired
	private UserMapper userMapper;
	
	@Autowired
	private BCryptPasswordEncoder bcryptPasswordEncoder;

	@Override
	public void addUser(User user) {
		user.setPasswd(this.bcryptPasswordEncoder.encode(user.getPasswd()));
		userMapper.insert(user);
	}
	
	//..omit..	
}	

Now that you have modified your Java source, compile it (mvn clean compile war: inplace) and rerun Tomcat.
This time the application is not loaded.
If you check the Tomcat log, the reason for the loading failure is that you can not inject BCryptPasswordEncoder into UserServiceImpl.
If you want to reference the BCryptPasswordEncoder outside the Authentication Provider, you have to modify the password encoder settings as shown below.

security.xml
<beans:bean id="bcryptPasswordEncoAccess Deniedder" 
  class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />

<authentication-manager>
  <authentication-provider>
    <jdbc-user-service data-source-ref="dataSource"
      users-by-username-query="select 
          email as username,passwd as password,1 as enabled
        from member 
        where email = ?"
      authorities-by-username-query="select 
          email as username,authority
        from authorities 
        where email = ?" />
    <password-encoder ref="bcryptPasswordEncoder" />
  </authentication-provider>
</authentication-manager>

Try re-running Tomcat and try to join.
This time, SQLException occurs because of the length of the password column in the membership table.
Change the passwd column of the member table as shown below.

Oracle
alter table member modify passwd varchar2(200);
MySQL
alter table member modify passwd varchar(200) not null;

After deleting all data of authorities and member tables, try join the membership.
Enter email at johndoe@gmail.org and enter 1111 for the password.

If the login succeeds, update the following.

Modify Account, Change Password, Bye

UsersController.java
@RequestMapping(value="/bye", method=RequestMethod.POST)
public String bye(String email, String passwd, HttpServletRequest req) 
        throws ServletException {
	User user = new User();
	user.setEmail(email);
	user.setPasswd(passwd);
	userService.bye(user);
	
	req.logout();

	return "redirect:/users/bye_confirm";
}
UserServiceImpl.java
@Override
public int editAccount(User user) {
	String encodedPassword = this.getUser(user.getEmail()).getPasswd();   
	boolean check = this.bcryptPasswordEncoder.matches(user.getPasswd(), encodedPassword);

	if (check == false) {
		throw new AccessDeniedException("The current password is incorrect.");
	}
	
	user.setPasswd(encodedPassword);

	return userMapper.update(user);
}

@Override
public int changePasswd(String currentPasswd, String newPasswd, String email) {
	String encodedPassword = this.getUser(email).getPasswd();
	boolean check = this.bcryptPasswordEncoder.matches(currentPasswd, encodedPassword);

	if (check == false) {
		throw new AccessDeniedException("The current password is incorrect.");
	}
	
	newPasswd = this.bcryptPasswordEncoder.encode(newPasswd);
	
	return userMapper.updatePasswd(encodedPassword, newPasswd, email);
}

@Override
public void bye(User user) {
	String encodedPassword = this.getUser(user.getEmail()).getPasswd();
	boolean check = this.bcryptPasswordEncoder.matches(user.getPasswd(), encodedPassword);

	if (check == false) {
		throw new AccessDeniedException("The current password is incorrect.");
	}
	
	userMapper.deleteAuthority(user.getEmail());
	userMapper.delete(user);
}

Remove login(String email, String passwd) method of UserService.java.