Skip to content

Commit

Permalink
fix: Token Reissue 시 access Token 만료시간으로 인해 재발급되지 않던 버그 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
redcarrot1 committed Feb 25, 2024
1 parent d5e8ae8 commit 9221ff2
Show file tree
Hide file tree
Showing 6 changed files with 26 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class TokenApi {
@PostMapping("reissue")
@Operation(summary = "access token 재발급", description = "access token과 refresh token을 재발급받습니다.")
public ApiResponse<TokenReissueResponse> accessTokenReissue(@RequestBody @Valid TokenReissueRequest request) {
TokenReissueResponse response = tokenReissueService.reissue(request.getAccessToken(), request.getRefreshToken());
TokenReissueResponse response = tokenReissueService.reissue(request.getRefreshToken());
return ApiUtils.success(response);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public TokenDto createTokenDto(String username) {
Date refreshTokenExpiredAt = createRefreshTokenExpirationTime(now);

String accessToken = createAccessToken(username, accessTokenExpiredAt);
String refreshToken = createRefreshTokenEntity(refreshTokenExpiredAt);
String refreshToken = createRefreshTokenEntity(username, refreshTokenExpiredAt);

return TokenDto.builder()
.grantType(GrantType.BEARER.getType())
Expand Down Expand Up @@ -86,11 +86,12 @@ private String createAccessToken(String username, Date expireDate) {
.compact();
}

private String createRefreshTokenEntity(Date expireDate) {
private String createRefreshTokenEntity(String username, Date expireDate) {
return Jwts.builder()
.setHeaderParam(Header.TYPE, Header.JWT_TYPE)
.setIssuer(issuer)
.setExpiration(expireDate)
.setSubject(username)
.signWith(key, SignatureAlgorithm.HS256)
.setHeaderParam(tokenTypeHeaderKey, TokenType.REFRESH.name())
.compact();
Expand All @@ -102,6 +103,10 @@ public Authentication getAuthenticationFromAccessToken(String accessToken) {
return UsernamePasswordAuthenticationToken.authenticated(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
}

public String getUsernameFromToken(String token) {
return parseClaims(token).getSubject();
}

private Claims parseClaims(String accessToken) {
try {
return Jwts.parserBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.playkuround.playkuroundserver.domain.auth.token.dto.response.TokenReissueResponse;
import com.playkuround.playkuroundserver.domain.auth.token.exception.InvalidRefreshTokenException;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -18,28 +17,16 @@ public class TokenReissueService {
private final RefreshTokenRepository refreshTokenRepository;

@Transactional
public TokenReissueResponse reissue(String accessToken, String refreshToken) {
String username = getUsernameFromAccessToken(accessToken);
public TokenReissueResponse reissue(String refreshToken) {
String username = tokenManager.getUsernameFromToken(refreshToken);

validateRefreshToken(username, refreshToken);
if (!refreshTokenRepository.existsByUserEmail(username)) {
throw new InvalidRefreshTokenException();
}

TokenDto tokenDto = tokenManager.createTokenDto(username);
tokenService.registerRefreshToken(username, tokenDto.getRefreshToken());

return TokenReissueResponse.from(tokenDto);
}

private String getUsernameFromAccessToken(String accessToken) {
Authentication authentication = tokenManager.getAuthenticationFromAccessToken(accessToken);
return authentication.getName();
}

private void validateRefreshToken(String userEmail, String refreshToken) {
if (!tokenManager.isValidateToken(refreshToken)) {
throw new InvalidRefreshTokenException();
}
if (!refreshTokenRepository.existsByUserEmail(userEmail)) {
throw new InvalidRefreshTokenException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@
@AllArgsConstructor
public class TokenReissueRequest {

@NotBlank(message = "accessToken은 필수 입력 값입니다.")
@Schema(description = "Access Token", example = "eyJ0eXAiOiJ..{생략}..")
private String accessToken;

@NotBlank(message = "refreshToken은 필수 입력 값입니다.")
@Schema(description = "Refresh Token", example = "eyJ0aAdJ13..{생략}..")
private String refreshToken;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ void reissueSuccess() throws Exception {
userRepository.save(user);
TokenDto tokenDto = userLoginService.login(user.getEmail());

TokenReissueRequest tokenReissueRequest = new TokenReissueRequest(tokenDto.getAccessToken(), tokenDto.getRefreshToken());
TokenReissueRequest tokenReissueRequest = new TokenReissueRequest(tokenDto.getRefreshToken());
String request = objectMapper.writeValueAsString(tokenReissueRequest);

// expected
Expand All @@ -84,11 +84,7 @@ void reissueSuccess() throws Exception {
@DisplayName("토큰 재발급 실패 : 유효하지 않은 refreshToken")
void reissueFailByInvalidateRefreshToken() throws Exception {
// given
User user = TestUtil.createUser();
userRepository.save(user);
TokenDto tokenDto = userLoginService.login(user.getEmail());

TokenReissueRequest tokenReissueRequest = new TokenReissueRequest(tokenDto.getAccessToken(), "invalidateRefreshToken");
TokenReissueRequest tokenReissueRequest = new TokenReissueRequest("invalidateRefreshToken");
String request = objectMapper.writeValueAsString(tokenReissueRequest);

// expected
Expand All @@ -113,7 +109,7 @@ void reissueFailByNotFoundRefreshToken() throws Exception {
TokenDto tokenDto = userLoginService.login(user.getEmail());
refreshTokenRepository.deleteAll();

TokenReissueRequest tokenReissueRequest = new TokenReissueRequest(tokenDto.getAccessToken(), tokenDto.getRefreshToken());
TokenReissueRequest tokenReissueRequest = new TokenReissueRequest(tokenDto.getRefreshToken());
String request = objectMapper.writeValueAsString(tokenReissueRequest);

// expected
Expand All @@ -128,53 +124,4 @@ void reissueFailByNotFoundRefreshToken() throws Exception {
.andExpect(jsonPath("$.errorResponse.message").value(ErrorCode.INVALID_TOKEN.getMessage()))
.andDo(print());
}

@Test
@DisplayName("토큰 재발급 실패 : accessToken이 유효하지 않음")
void reissueFailByInvalidAccessToken() throws Exception {
// given
User user = TestUtil.createUser();
userRepository.save(user);
TokenDto tokenDto = userLoginService.login(user.getEmail());

TokenReissueRequest tokenReissueRequest = new TokenReissueRequest("invalidAccessToken", tokenDto.getRefreshToken());
String request = objectMapper.writeValueAsString(tokenReissueRequest);

// expected
mockMvc.perform(post("/api/auth/reissue")
.contentType(MediaType.APPLICATION_JSON)
.content(request)
)
.andExpect(status().isUnauthorized())
.andExpect(jsonPath("$.isSuccess").value(false))
.andExpect(jsonPath("$.errorResponse.status").value(ErrorCode.INVALID_TOKEN.getStatus().value()))
.andExpect(jsonPath("$.errorResponse.code").value(ErrorCode.INVALID_TOKEN.getCode()))
.andExpect(jsonPath("$.errorResponse.message").value(ErrorCode.INVALID_TOKEN.getMessage()))
.andDo(print());
}

@Test
@DisplayName("토큰 재발급 실패 : 존재하지 않는 유저")
void reissueFailByNotFoundUser() throws Exception {
// given
User user = TestUtil.createUser();
userRepository.save(user);
TokenDto tokenDto = userLoginService.login(user.getEmail());
userRepository.deleteAll();

TokenReissueRequest tokenReissueRequest = new TokenReissueRequest(tokenDto.getAccessToken(), tokenDto.getRefreshToken());
String request = objectMapper.writeValueAsString(tokenReissueRequest);

// expected
mockMvc.perform(post("/api/auth/reissue")
.contentType(MediaType.APPLICATION_JSON)
.content(request)
)
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.isSuccess").value(false))
.andExpect(jsonPath("$.errorResponse.status").value(ErrorCode.USER_NOT_FOUND.getStatus().value()))
.andExpect(jsonPath("$.errorResponse.code").value(ErrorCode.USER_NOT_FOUND.getCode()))
.andExpect(jsonPath("$.errorResponse.message").value(ErrorCode.USER_NOT_FOUND.getMessage()))
.andDo(print());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
import com.playkuround.playkuroundserver.domain.auth.token.dto.TokenDto;
import com.playkuround.playkuroundserver.domain.auth.token.dto.response.TokenReissueResponse;
import com.playkuround.playkuroundserver.domain.auth.token.exception.InvalidRefreshTokenException;
import com.playkuround.playkuroundserver.domain.auth.token.exception.InvalidTokenException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
Expand All @@ -37,18 +36,17 @@ class TokenReissueServiceTest {
@DisplayName("토큰 재발급 성공")
void reissueSuccess() {
// given
Authentication authenticated = UsernamePasswordAuthenticationToken.authenticated("username", null, null);
when(tokenManager.getAuthenticationFromAccessToken("accessToken")).thenReturn(authenticated);
when(tokenManager.isValidateToken("refreshToken")).thenReturn(true);
when(tokenManager.getUsernameFromToken("refreshToken")).thenReturn("username");
when(refreshTokenRepository.existsByUserEmail("username")).thenReturn(true);

TokenDto tokenDto = new TokenDto("newGrantType", "newAccessToken", "newRefreshToken", null, null);
TokenDto tokenDto = new TokenDto("newGrantType", "newAccessToken", "newRefreshToken",
null, null);
when(tokenManager.createTokenDto("username")).thenReturn(tokenDto);

doNothing().when(tokenService).registerRefreshToken("username", "newRefreshToken");

// when
TokenReissueResponse response = tokenReissueService.reissue("accessToken", "refreshToken");
TokenReissueResponse response = tokenReissueService.reissue("refreshToken");

// then
assertThat(response.getAccessToken()).isEqualTo("newAccessToken");
Expand All @@ -59,25 +57,21 @@ void reissueSuccess() {
@Test
@DisplayName("토큰 재발급 실패 : 유효하지 않은 refreshToken")
void reissueFailByInvalidateToken() {
Authentication authenticated = UsernamePasswordAuthenticationToken.authenticated("username", null, null);
when(tokenManager.getAuthenticationFromAccessToken("accessToken")).thenReturn(authenticated);
when(tokenManager.isValidateToken("refreshToken")).thenReturn(false);
when(tokenManager.getUsernameFromToken("refreshToken")).thenThrow(InvalidTokenException.class);

// when
assertThatThrownBy(() -> tokenReissueService.reissue("accessToken", "refreshToken"))
.isInstanceOf(InvalidRefreshTokenException.class);
assertThatThrownBy(() -> tokenReissueService.reissue("refreshToken"))
.isInstanceOf(InvalidTokenException.class);
}

@Test
@DisplayName("토큰 재발급 실패 : refreshToken이 저장소에 존재하지 않음")
void reissueFailByNotFoundRefreshToken() {
Authentication authenticated = UsernamePasswordAuthenticationToken.authenticated("username", null, null);
when(tokenManager.getAuthenticationFromAccessToken("accessToken")).thenReturn(authenticated);
when(tokenManager.isValidateToken("refreshToken")).thenReturn(true);
when(tokenManager.getUsernameFromToken("refreshToken")).thenReturn("username");
when(refreshTokenRepository.existsByUserEmail("username")).thenReturn(false);

// when
assertThatThrownBy(() -> tokenReissueService.reissue("accessToken", "refreshToken"))
assertThatThrownBy(() -> tokenReissueService.reissue("refreshToken"))
.isInstanceOf(InvalidRefreshTokenException.class);
}
}

0 comments on commit 9221ff2

Please sign in to comment.