Last Modified 2021.11.25

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>

The <password-encoder hash="bcrypt" /> tells Spring to use BCryptPasswordEncoder, which implements the PasswordEncoder interface and uses the bcrypt algorithm.

If you try to log in as johndoe@gmail.org/1111 after rerunning Tomcat, the login will fail because the unencrypted password, 1111, is stored in the member table.

Delete all data of authorities and member tables.

sqlplus java/school

delete from authorities;

delete from member;

commit;

Sign up again with email johndoe@gmail.org and password 1111.

Even if you try to log in again after signing up, you cannot log in because the system stores the password in plain text 1111 in the member table.

Modify the UserServiceImpl class 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..	
}	

Recompile it (mvn clean compile war: inplace) and rerun Tomcat. This time the application is not loaded. Tomcat log tells the reason for the loading failure is that Spring can not inject BCryptPasswordEncoder into UserServiceImpl. If you want Spring to reference the BCryptPasswordEncoder outside the Authentication Provider, you must modify the password encoder configuration 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>

Restart Tomcat and try to sign up. This time, SQLException occurs because of the length of the passwd column in the member table. Increase 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;

Delete all data of authorities and member tables and sign up with email johndoe@gmail.org and password 1111.

If the login succeeds, update the following methods.

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

The login(String email, String passwd) method of UserService class is unnecessary, so remove it.