Spring Security로 회원가입 / 로그인 / 로그아웃을 구현할때 BCryptPasswordEncoder를 사용했었습니다. 그때, DB에 Password를 암호화해서 저장하고... 실제로 로그인 시도가 있을때 이를 비교해서 인증된 사용자 인지 아닌지를 판단해서 그 이후 로직을 처리했습니다.
그때, 고려했던 방법은 아래의 2가지 였습니다. 그 중에 뒤에방식을 사용했었는데요... 왜 그렇게 구현했는지 실제로 눈으로 확인해 보겠습니다.
- Form으로 입력된 Password를 암호화 하여, DB의 암호화 된 Password와 equals로 비교하여 인증
- Form으로 입력된 Password를 DB의 암호화 된 Password와 자체 제공하는 메서드를 통해서 비교하여 인증
1. equals( )
실제로 DB에서 Password를 가져오는 부분은 MyUserDetailsService에서 가져오지만 실질적인 검증에 대한 부분은 MyAuthenticationProvider에서 구현합니다. equals( )로 구현한 소스를 살펴보면...
[MyAuthenticationProvider.java]
@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
@Autowired
MyUserDetailsService myUserDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String loginId = (String) authentication.getPrincipal();
String loginPass = (String) authentication.getCredentials();
System.out.println(loginId);
System.out.println(loginPass);
System.out.println(authentication);
MyUserDetails mud = (MyUserDetails) myUserDetailsService.loadUserByUsername(loginId);
BCryptPasswordEncoder passEncoder = new BCryptPasswordEncoder();
String encryptPass = passEncoder.encode(loginPass);
System.out.println(encryptPass);
if(mud == null || !mud.getPassword().equals(encryptPass)) return null;
//권한 가져오는 로직
ArrayList<String> userRole = myUserDetailsService.getUserRoleByUserId(mud.getUserId());
ArrayList<GrantedAuthority> authorities = new ArrayList<>();
for(String eachRole : userRole) {
authorities.add(new SimpleGrantedAuthority(eachRole));
}
//(principal, credentials, authorities)
return new UsernamePasswordAuthenticationToken(loginId, loginPass, authorities);
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
form에서 입력받은 loginPass는 BCryptPasswordEncoder의 encode 메서드를 사용해서 암호화를 한 후, UserDetails로 정의된 DTO를 통해 받아온 정보를 ... mud.getPassword().equals(encryptPass)와 비교해서 True일 경우 인증을 허가합니다.
실제로 실행하여 localhost/home을 통해서 로그인을 수행하면 단 한번도 성공할 수 없습니다. 비록 정확한 password를 입력한다 해도 말이죠...!!
그 이유는 무엇일까요?? 그래서 console에 sysout을 찍어보았습니다.
위의 내용을 확인해 보면, 로그인을 할때마다 동일한 Password에 대해서 ... 암호화한 결과가 다르게 발생합니다.
try 1 : $2a$10$/2YfxB7Vn9fyGDqtB.LtpeE2HB/J30po9sljeL9oQD7p1x1kAK1HS
try 2 : $2a$10$Z8jJpbYEBQESQ8l87XbP1.f9pWoICZyjb3Idx/g59ACQ83yEh8MM2
이러니 실패를 할 수밖에 없습니다. 그렇다면 어떻게 해야할까요??
2. matches( )
이번에는 BCryptPasswordEncoder의 다른 메서드를 확인해 보겠습니다.
보면... Verify the encoded password obtained from storage matches the submetted raw password after it too is encodes. DB에 저장된 암호화 된 password와 form을 통해서 받은 plain password를 비교해 주는 아주 딱 맞는 메서드 입니다.
[MyAuthenticationProvider.java]
@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
@Autowired
MyUserDetailsService myUserDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String loginId = (String) authentication.getPrincipal();
String loginPass = (String) authentication.getCredentials();
System.out.println(loginId);
System.out.println(loginPass);
System.out.println(authentication);
MyUserDetails mud = (MyUserDetails) myUserDetailsService.loadUserByUsername(loginId);
BCryptPasswordEncoder passEncoder = new BCryptPasswordEncoder();
Boolean matchPass = passEncoder.matches(loginPass, mud.getPassword());
if(mud == null || !matchPass) return null;
//권한 가져오는 로직
ArrayList<String> userRole = myUserDetailsService.getUserRoleByUserId(mud.getUserId());
ArrayList<GrantedAuthority> authorities = new ArrayList<>();
for(String eachRole : userRole) {
authorities.add(new SimpleGrantedAuthority(eachRole));
}
//(principal, credentials, authorities)
return new UsernamePasswordAuthenticationToken(loginId, loginPass, authorities);
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
Boolean으로 matches( )의 결과를 저장하고 True일 경우 인증을 정상적으로 수행합니다. 그럼 결과를 살펴보면... 역시나 정상적으로 실행됩니다.
이 부분은 Spring Security 3번째 글에서 확인을 했었습니다. 그럼 즐거운 개발되세요~~
-Ayotera Lab-
댓글