스프링 시큐리티 - 비밀번호 암호화
비밀번호는 단순 텍스트로 저장해서는 안 된다.
패스워드 인코더 설정
<authentication-provider> 엘리먼트에 <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>
hash="bcrypt"로 설정하면 인터페이스 PasswordEncoder의 구현체 중 bcrypt 알고리즘을 사용하는 BCryptPasswordEncoder가 설정된다.
톰캣을 재실행하고 hong@gmail.org/1111으로 로그인을 시도하면 이전과 달리 로그인이 실패하게 된다.
회원 테이블에 암호화가 안된 1111이 저장되어 있기 때문이다.
권한 테이블과 회원 테이블을 모두 삭제한다.
sqlplus java/school delete from authorities; delete from member; commit;
이메일은 hong@gmail.org, 비밀번호는 1111로 회원가입한다.
가입 후 로그인을 시도하면 역시 로그인이 실패한다.
회원 테이블에 비밀번호가 여전히 단순 텍스트 1111로 저장되기 때문이다.
비밀번호가 암호화하여 저장하기 위해 UserServiceImpl.java를 수정해야 한다.
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);
}
//..생략..
}
자바 소스를 수정했으니
컴파일(mvn clean compile war:inplace)하고 톰캣을 재실행한다.
이번에는 애플리케이션이 로딩되지 않는다.
톰캣 로그를 확인해 보면,
로딩에 실패한 원인은 UserServiceImpl에 BCryptPasswordEncoder를 주입할 수 없기 때문이다.
BCryptPasswordEncoder를 Authentication Provider 밖에서도 참조하기 원한다면
패스워드 인코더 설정을 수정해야 한다.
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>
톰캣을 재실행한 후 회원가입을 시도한다.
이번에는 회원 테이블의 패스워드 컬럼의 길이때문에 SQLException이 발생하게 된다.
member 테이블의 passwd 컬럼을 변경한다.
Oracle
alter table member modify passwd varchar2(200);
MySQL
alter table member modify passwd varchar(200) not null;
이메일은 im@gmail.org, 비밀번호는 1111로 회원가입한다.
이제는 로그인이 되어야 한다.
회원 수정, 비밀번호 변경, 탈퇴
회원 수정, 비밀번호 변경, 탈퇴를 수정해야 한다.
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("현재 비밀번호가 틀립니다.");
}
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("현재 비밀번호가 틀립니다.");
}
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("현재 비밀번호가 틀립니다.");
}
userMapper.deleteAuthority(user.getEmail());
userMapper.delete(user);
}
UserService.java의 login(String email, String passwd) 메소드는 필요 없으니 삭제한다.
UserServiceImpl.java와 UserMapper.java와 UserMapper.xml에서도 이 메소드와 관련된 부분을 함께 삭제한다.
