-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Mod] 토큰 비유효시 반환값 변경 및 토큰 검증 로직 최적화 #167
Conversation
- 토큰 비유효시 401 상태코드를 반환하고 status로 구분하도록 코드 수정 (accessTokenEmpty, acceessTokenInvalid, refreshTokenInvalid) - 토큰 검증 로직 최적화
아 그리고, NO_CHECK_URLS에 "/**/additional-info"도 추가해두었습니다
|
실패하는 테스트코드가 있어 PR이 자동으로 닫혔습니다. |
// 리프레시 토큰이 있고, 유효할 경우 엑세스토큰 재발급 | ||
// 이때 조건절의 isRefreshTokenValid에서 토큰이 유효하지 않으면 InvalidRefreshTokenException 발생 | ||
if (refreshToken != null && jwtTokenProvider.isRefreshTokenValid(refreshToken)) { | ||
// 리프레시토큰의 경우 토큰의 유효성 뿐만 아니라 DB에 등록되어 있는지도 확인해야 함 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
전에 디스코드에서 토큰 유효성을 checkRefreshTokenAndReIssueAccessToken
메서드 내에서 처리하는 쪽으로 말씀하셨던 것 같은데 이 코드에서는 if문에서 처리하게 된 이유가 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// refreshToken로 검색 후 accessToken 재발급 후 전송
public void checkRefreshTokenAndReIssueAccessToken(HttpServletResponse response, String refreshToken) throws IOException {
if (!jwtTokenProvider.isTokenValid(refreshToken)) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid Refresh token.");
}
userRepository.findByRefreshToken(refreshToken) // refreshToken으로 유저 찾기
.ifPresent(user -> {
String newAccessToken = jwtTokenProvider.createAccessToken(user.getEmail(), user.getId()); // accessToken 생성
log.info("New accessToken issued: " + newAccessToken); // 재발급된 accessToken 출력
jwtTokenProvider.sendAccessToken(response, newAccessToken); // accessToken 전송
// jwtTokenProvider.sendAccessToken(response, jwtTokenProvider.createAccessToken(user.getEmail(), user.getId())); // accessToken 생성 후 전송
});
}
위 기존코드에서 토큰 자체가 유효한지 검증(isTokenValid)를 했는데 비유효할 경우 response.sendError를 날렸음에도 아래 DB를 확인하는 코드가 실행될 여지가 있어 뺐습니다. 그런데 지금 보니까 response.sendError안에 return만 넣어주면 될 것 같긴한데 이를 차치하더라도,
아래 엑세스토큰 검증하는 코드랑 조건절이 유사해서 일관성, 가독성 측면에서도 더 낫다고 생각해 토큰 자체가 유효한지 판단하는 로직을 밖으로 뺐습니다. 리프레시토큰 검증할 때는 if문 내부의 메서드에서 토큰 자체가 유효한지 판단하고, 엑세스토큰 검증할 때는 if문 밖에서 토큰 자체가 유효한지 판단하는게 어색하다고 느꼈습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
네~ 코드 가독성 측면에서 이 방법이 더 좋을 것 같네요!
checkAccessTokenAndAuthentication(request, response, filterChain); | ||
return; | ||
} else if (accessToken != null){ // 엑세스 토큰이 있는데 유효하지 않은 경우 InvalidAccessTokenException 발생 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기서 accesstoken이 유효하지 않으면 if (accessToken != null && jwtTokenProvider.isAccessTokenValid(accessToken))
에서 isAccessTokenValid(accessToken)
로 예외 발생하면서 if 조건의 평가가 중단됩니다.
그래서 catch (Exception e)
에서 InvalidAccessTokenException을 처리하고 있으므로, else if까지 코드가 도달할 일이 없는걸로 알고 있는데 혹시 어떻게 생각하시나요?
위에서 유효하지않은 refreshtoken이 처리되는 경우도
if (refreshToken != null && jwtTokenProvider.isRefreshTokenValid(refreshToken))
에서 예외(InvalidRefreshTokenException)가 발생해서 catch (Exception e)
에서 예외가 처리하는 방식으로 코드를 구현하신 것 같아서요.
같은 방식으로 처리하면 어떨까 싶어서 제안드립니다~!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아 말씀하신 부분은 의도했습니다. 메서드명때문에 혼란을 드린 것 같네요
if (accessToken != null && jwtTokenProvider.isAccessTokenValid(accessToken))
로 엑세스토큰 자체가 유효하지 않으면 else if로 도달하지 않게 의도하여 코드 작성하였고
유효하면 첫 번째 else if문에 도달해 checkAccessTokenAndAuthentication
메서드가 실행되고 해당 메서드에서는 검증이 일어나지 않고 유저의 권한 정보만 저장하고 스프링 시큐리티 필터체인에서 다음 필터로 넘어가게 됩니다.
checkAccessTokenAndAuthentication
의 메서드명을 saveAuthenticationandDoFilter
정도로 수정하겠습니다.
그래서 refreshToken 검증하는 코드랑 흐름이 유사합니다.
if 조건절에서 null인지 먼저 판단하고 토큰 자체의 유효성을 판단한 뒤, 유효하다면 각자 필요한 메서드가 실행됩니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
유효하면 첫 번째 else if문에 도달해 checkAccessTokenAndAuthentication메서드가 실행되고 해당 메서드에서는 검증이 일어나지 않고 유저의 권한 정보만 저장하고 스프링 시큐리티 필터체인에서 다음 필터로 넘어가게 됩니다.
이 부분이 무슨 의미일까요?
유효하면 else if (accessToken != null)
문에 도달한다는 건가요? 유효한 경우에는 if (accessToken != null && jwtTokenProvider.isAccessTokenValid(accessToken))
로 들어가는 것 아닌가요? else if 문으로 넘어오는 경우가 어떤 경우를 의도하고 만드신 것인지 궁금합니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아 답변드리면서 깨달았네요. jwtTokenProvider.isAccessTokenValid(accessToken)
에서 토큰 자체가 유효하지않으면 Exception을 날리기 때문에 else if랑 역할이 완전히 겹치네요.
코드 검토가 부족했던 것 같습니다.
else if문을 빼면 코드 가독성도 훨씬 좋아지는 것 같네요! else if문은 빼고 푸시하겠습니다~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 알겠습니다~!
그리고 |
아 앞의 코멘트에 설명이 부족했네요. 리프레시토큰 확인하고 if (accessToken == null)와 if (accessToken != null && jwtTokenProvider.isAccessTokenValid(accessToken))의 순서 변경을 말씀 드렸던 것입니다. null을 먼저 확인하면 뒤에서 null인지는 확인 안하고 valid 여부만 확인해도 되지않을까해서요. |
근데 그거는 순서 변경 하든 안하든 소요시간이 똑같아보입니다. 차이가 없어보입니다. |
네 저 의도로 질문한 것 맞습니다. |
아 이거는 근데 저는 지금 코드가 더 낫다고 생각하는게 취향의 영역이긴한데 리프레시토큰 검증하는 if문이랑 틀이 똑같아서 처음 보는 사람이 코드 봤을 때 더 이해하기 쉽다고 생각하긴 합니다. 물론 협업하는 사람이 진서님밖에 없긴 하지만요 |
네 알겠습니다. 코드의 가독성도 중요해서 성능적으로 크게 차이는 없을 것 같아서 그대로 진행해도 괜찮을 것 같습니다 |
넵 성능적으로는 전혀 차이 없습니다. 성능차이가 있다고 하더라고 O(1)수준이여서 고려하지 않으셔도 될 것 같습니다. 그러면
해서 푸시 넣겠습니다~ |
수고 많으셨습니다~!! |
수정한 코드
(
accessTokenEmpty
,acceessTokenInvalid
,refreshTokenInvalid
)변경 사항
자세한 로직은 주석 최대한 상세하게 작성하였으니 확인하시면 코드 파악하기 편하실 것 같습니다
ApiResponseForm
의status
로 구분하는 것으로 변경하였음.accessTokenInvalid
", 리프레시 토큰 비유효시 "refreshTokenInvalid
", 토큰이 없을 시 "accessTokenEmpty
"를 status로 반환함.refreshToken != null
인지 확인 -> 토큰 자체가 유효한지 확인 -> DB에서 확인해서 리프레시 토큰 검증을 처리함.refreshToken == null
일 경우 조건절의refreshToken != null
이false
이므로 if문 내부로 진입하지 않고 바로 엑세스토큰 검증 로직으로 넘어감InvalidTokenException
이라는 예외가 발생해 두 토큰을 구분할 수 없었음.InvalidTokenException
을 상속한InvalidAccessTokenException
,InvalidRefreshTokenException
을 정의하고 비유효시 두 에러를 발생시키게 하였고,catch
문에서는InvalidTokenException
을catch
하고 캐치문 안에서isinstance
로 구분하였음.테스트 결과
[토큰이 없을 경우]

[엑세스 토큰이 있는데 만료됐을 경우]

[리프레시 토큰이 있는데 만료됐을 경우 - 엑세스토큰 없는 버전]

[리프레시 토큰이 있는데 만료됐을 경우 - 엑세스토큰 있는 버전]
