사실 로그인과 토큰 재발급을 나눠서 진행하려 했는데, 로그인 API가 사실 회원가입과 크게 다를 부분이 없어서 한꺼번에 다루기로 했다. 회원가입에서 save만 빠지면 사실상 로그인이기 때문에 복습 겸 다뤄보도록 하자. 회원가입 편을 보고 온다면 이해가 더 빠를 것 이기 때문에 한번 보고 오는 것을 추천한다.
회원가입할때에도 가입이 완료 후 JWT를 발급해주었기 때문에 코드가 매우 비슷하다. 로그인 부분에서는 Validate를 구현을 해놓았었는데 이 부분을 살펴보면
private void LOGIN_VALIDATE(UserDto.login request) {
userRepository.findByEmail(request.getEmail())
.orElseThrow(
() -> new CustomException(LOGIN_FALSE)
);
if (!passwordEncoder.matches(
request.getPw(),
userRepository.findByEmail(request.getEmail())
.orElseThrow(
() -> new CustomException(LOGIN_FALSE)
).getPw())
) {
throw new CustomException(LOGIN_FALSE);
}
}
요청된 정보를 서비스 로직이 실행 전 가장 먼저 검증 단계를 거치는데, 서버의 DB에 해당 이메일이 존재하는지 여부를 먼저 살피고 없다면 CustomException을 통해 로그인 실패 코드를 보내준다. CustomeException에 궁금하다면 아래 글을 읽어보도록 하자.
이메일이 존재하다면 PasswordEncoder에서 지원하는 matches를 통해 현재 요청받은 비밀번호와 DB에 저장되어있는 암호화된 비밀번호가 동일한지에 대해서 검증을 하게되며 이 또한 동일하지 않는다면 CustomException이 발생하게 된다.
이정도의 간단한 Validation을 구현하였고 만약 프로젝트를 진행 중이라면 해당 보안에 맞게 더 많은 검증을 구현하면 된다. 검증이 완료되었다면 회원가입 때와 마찬가지로 유저의 아이디와 비밀번호로 authentication을 만들어주고 SecurityContextHolder에 등록해준다.
또한 AccessToken과 RefreshToken을 발급해주는데 자세한 사항은 회원가입편을 참고하도록 하자.
마찬가지로 유저의 이메일과 RefreshToken을 가지고 지속시간을 14일로 설정 후 Redis에 저장해준다. 여기서 Response는 HttpStatus.Ok로 보내주었는데 이는 200 코드를 보내준다는 의미이다. 회원가입의 경우 201(Create)였는데 각각의 상태에 맞게 성공 시 코드도 구분하는 것이 좋다 해서 나누게 되었다.
설명은 회원가입편에서 돌아보고 로그인 구현에 대한 설명은 여기서 마치겠다. 이제 RefreshToken을 이용해 AccessToken을 재발급 하는 API를 살펴보자 !
아래 이미지는 accessToken 재발급 API
재발급 로직
1. refreshToken에서 사용자의 이메일을 가져옴
2. 사용자의 이메일을 이용해 redis에서 해당 유저의 RefreshToken을 가져옴
3. 가져온 RefreshToken이 null 이거나, redis에 저장된 RefreshToken과 요청받은 RefreshToken이 다른 경우 throw를 던짐
4. 검증을 통과했다면 새로운 AccessToken 발급
accessToken을 재발급하는 API는 사실 별거 없다. username에 권한 정보를 담지 않은 RefreshToken에서 사용자의 이메일을 담고 rtkInRedis에 redisDao.getvalues를 통해 해당 유저가 가지고 있는 RefreshToken을 가져온다. 저번 편에서 설명했듯이 redis를 Spring에서 사용하는 방법은 여러 가지가 있기 때문에 접근하는 법에 있어서는 개발자가 편한 데로 하면 될 것이다.
rtkInRedis에 담은 정보를 토대로 검증을 진행하는데 null이거나 redis에 저장된 RefreshToken과 사용자가 요청한 RefreshToken이 다른 경우 throw를 던지며 410 상태 코드를 보내준다, 다만 에러코드의 경우 개발자와 클라이언트가 조율해서 더 나은 상태 코드로 구현하도록 하자.
검증을 통과한 경우 return에서 새로운 AccessToken을 재발급해준다.
RefreshToken의 경우 헤더로 요청받게끔 넘겼으며 요청받은 정보를 바로 Service로 return 해준다.
다음 편에서는 로그아웃 API와 로그아웃이 된 상태를 알아보기 위해 간단한 정보 조회 API를 개발해볼 것이다. 로그아웃을 위해 atk의 블랙리스트를 구현하고 rtk를 삭제하는 로직, Jwt의 Filter에서는 어떻게 처리하는지 알아보도록 하자!