카카오 간편 로그인을 하며 얻은 경험을 정리하는 글입니다.
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepositoryPort userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByEmail(username).map(this::createUserDetails).orElseThrow(()-> new UsernameNotFoundException(username + " -> not exist!"));
}
private UserDetails createUserDetails(User user) {
GrantedAuthority authority = new SimpleGrantedAuthority(user.getAuth().toString());
return new org.springframework.security.core.userdetails.User(String.valueOf(user.getId()),
user.getPassword(),
Collections.singleton(authority));
}
}
해당 부분에서 createUserDetails는 Security context에서 ID와 Password를 어떤걸로 할지 결정하는 부분이다. 해당 부분을 통해서 Security는
@AuthenticationPrincipal UserDetails userDetails
의 ID 값을 결정합니다.
String.valueOf(user.getId()) 라면 user id, email로 설정하면 email 이 됩니다.
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(register.getEmail(), register.getPassword());
이렇게 코드를 작성했더니 JWT 생성에 계속 실패하였다...
뭐가 문제일까 하다가 문득 이런 생각이 들었다.
1. UsernamePasswordAuthenticationToken 과정중 스프링 시큐리티 자체적으로 입력받은 값을 통해 DB로부터 email, password를 비교할 것이다.
2. 하지만 현재 내가 UsernamePasswordAuthenticationToken password는 encode 되어 들어간 값이다.
3. passwordEncoder가 입력받은 패스워드와 db내 패스워드와 일치여부를 비교한다.
4. (암호화된 패스워드와, 암호화전 패스워드)가 와야 성공일텐데 현재는 (암호화된 패스워드와, 암호화된 패스워드) 여서 실패한다.
이런 생각을 하고나서 생각해보니 간편 로그인이 아닌 기존 로그인의 경우 password 입력값이 평문이라 문제가 없었지만 간편 로그인 방식은 다 암호화된 값이라 문제가 생겼겠구나 생각을 했다. 그래서 password를 어떤 유일한 식별값으로 암호화해서 넣고 UsernamePasswordAuthenticationToken에 유일한 식별값을 password로 세팅을 해주고 실행을 했더니 정상적으로 JWT를 발급할 수 있었다.
실제로 글을 찾아보니
retrieveUser()는 UserDetailsService객체를 통해 로그인 요청한 유저의 UserDetails 객체를 가져온다
additionalAuthenticationChecks()메소드는 입력받은 정보(username, credetial)와 userDetails객체의 정보와 비교해 인증을 체크하는 메소드이다. 실질적으로 DB의 데이터와 id, 비밀번호를 입력한 값과 비교하는 곳이라 할 수 있다.
즉 1번과 연결되는데,
1. UsernamePasswordAuthenticationToken에 email, 평문 password 넘김
2. loadByUsername 실행(UsernamePasswordAuthenticationToken에 준 email 로 찾음)
3. 정보를 찾음.
4. createUserDetails 통해 만든 유저 정보와 비교.
5. id 일치 -> (암호화된 password, 평문 password) 비교 성공하면 JWT 생성
Security JWT 발급에 대해 더욱 이해가 높아지는 중요한 경험이었다.