이제 백엔드쪽 로직은 AWS 서버에 배포해놓은 상태이다.
프론트 담당자분들이 각자 맡은 부분을 구현하면서 백엔드 서버와 통신했을때 응답이 제대로 오는지, 응답을 가지고 프론트쪽 처리가 제대로 되는지 확인하면서 작업하고있다.
통신을 하다보니 코드에서 몇가지 오류가 발생해서 수정 작업을 진행했다.
로그아웃시 리프레시토큰 검증 로직 제외
로그인 검증에 성공하면 리프레시토큰을 발급해서 Redis에 키-값(memberId - 리프레시토큰) 형태로 저장하고 있었다.
로그아웃을 요청(쿠키에 리프레시토큰 첨부)하면 해당 리프레시 토큰 값을 무효화 시키는방법으로 로그아웃을 구현했었다.
"Key" : "Value" || "memberId" : "리프레시토큰값" --(로그아웃요청)---> "memberId" : "logout"
그런데 문제는, 만료된 리프레시토큰을 가지고 로그아웃을 요청했을때 발생했다.
리프레시토큰에서 memberId를 조회하는 로직이 들어있어서 만료된 토큰에서 해당 정보를 가져올 수 없어서 서버 에러가 발생했다.
로그아웃은 리프레시토큰을 비활성화 시키는 기능만 있으면 되므로, 검증 로직은 빼고 토큰 자체를 비활성화 하는 방식으로 변경했다.
(변경 전) "memberId" : "리프레시토큰값" --(로그아웃요청)---> "memberId" : "logout"
(변경 후) "리프레시 토큰값" : "login" --(로그아웃요청)---> "리프레시 토큰값" : "logout"
이렇게 되면 처음 리프레시 토큰을 발급할때에도 Redis 저장소에 "리프레시 토큰값" : "login" 형태로 저장해주어야하므로 해당 형태로 변경해주었다.
처음 "memberId" : "리프레시토큰값" 으로 형태를 설계했던 이유는 한 회원은 한곳에서만 로그인이 가능하고 새로 로그인하면 이전의 리프레시 토큰을 비활성화 시키기 위함이었다. 그런데 이 경우 토큰이 만료되었을때의 오류가 생기기도 했고(이 부분을 따로 에러처리를 하면 가능할것같기는 하다.), 프로젝트 시연할 때 샘플 아이디와 비밀번호를 제공하게 되는데 만약 기존 방식을 사용하게되면 여러 사람들이 해당 아이디를 사용하지 못하는 문제가 생긴다.
따라서 이번 프로젝트에서는 변경된 형태로 데이터를 저장하는게 더 적합할것같기는 하다.
1. 토큰 발급 로직 수정
// JwtTokenizer 코드 일부. refreshToken을 키로, 로그인 상태를 값으로 저장하고있다.
public String delegateRefreshToken(Member member) {
String subject = member.getMemberId().toString();
Date expiration = getTokenExpiration(getRefreshTokenExpirationMinutes());
String base64EncodedSecretKey = encodeBase64SecretKey(getSecretKey());
String refreshToken = generateRefreshToken(subject, expiration, base64EncodedSecretKey);
redisTemplate.opsForValue().set(refreshToken, "login", getRefreshTokenExpirationMinutes(), TimeUnit.MINUTES);
return refreshToken;
}
2. 로그아웃 로직 수정
// MemberService 코드 일부. 액세스 토큰과 리프레시 토큰의 값을 logout으로 변경했다. (시간은 추후 변경예정)
@Override
public void logout(HttpServletRequest request) {
String accessToken = request.getHeader("Authorization").replace("Bearer ", "");
String refreshToken = getCookieValue(request, "RefreshToken");
RedisTemplate<Object, Object> redisTemplate = jwtTokenizer.getRedisTemplate();
redisTemplate.opsForValue().set(accessToken, "logout", 5, TimeUnit.MINUTES);
redisTemplate.opsForValue().set(refreshToken, "logout", 300, TimeUnit.MINUTES);
}
3. 액세스토큰 재발급 로직 수정
// MemberService 코드 일부. redis에서 리프레시토큰값을 가져와서 없거나(만료된 경우), login이 아닌경우(로그아웃한 경우)에러를 던진다.
@Override
public HttpServletResponse checkRefreshAndReIssueAccess(HttpServletRequest request, HttpServletResponse response) {
String refreshToken = getCookieValue(request, "RefreshToken");
String memberId = jwtTokenizer.getMemberIdFromRefreshToken(refreshToken);
RedisTemplate<Object, Object> redisTemplate = jwtTokenizer.getRedisTemplate();
String tokenStatus = (String) redisTemplate.opsForValue().get(refreshToken);
if (tokenStatus == null) {
throw new BusinessLogicException(ExceptionCode.INVALID_REFRESH_TOKEN_STATE);
}
else if (tokenStatus.equals("login")) {
String memberId = jwtTokenizer.getMemberIdFromRefreshToken(refreshToken);
String accessToken = jwtTokenizer.delegateAccessToken(memberRepository.findById(Long.parseLong(memberId)).orElseThrow());
response.setHeader("Authorization", "Bearer " + accessToken);
} else {
throw new BusinessLogicException(ExceptionCode.LOGOUT);
}
return response;
}
'부트캠프 개발일기 > Main-Project' 카테고리의 다른 글
108일차: Main-Project Day 15 (주문 정보 조회, FetchType, LazyInitializationException) (0) | 2023.07.18 |
---|---|
107일차: Main-Project Day 14 (마이페이지 API 설계) (0) | 2023.07.17 |
105일차: Main-Project Day 12 (권한 설정, 코드 리팩토링) (0) | 2023.07.13 |
104일차: Main-Project Day 11 (OAuth2.0 회원가입+로그인 로직 구현) (0) | 2023.07.12 |
103일차: Main-Project Day 10 (프론트엔드 쿠키 저장 안됨 해결!) (0) | 2023.07.11 |