diff --git a/server/.gitignore b/server/.gitignore index d42023f..959f866 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -6,6 +6,7 @@ # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 HELP.md +bin/** # User-specific stuff .idea/**/workspace.xml diff --git a/server/src/intTest/java/com/ticketing/server/movie/aop/TicketLockAspectTest.java b/server/src/intTest/java/com/ticketing/server/movie/aop/TicketLockAspectTest.java index cdac364..1c344bf 100644 --- a/server/src/intTest/java/com/ticketing/server/movie/aop/TicketLockAspectTest.java +++ b/server/src/intTest/java/com/ticketing/server/movie/aop/TicketLockAspectTest.java @@ -49,7 +49,7 @@ class TicketLockAspectTest { assertAll( () -> assertThat(result1).isNotEqualTo(result2), - () -> assertThat(unlockCount > 1).isTrue() + () -> assertThat(unlockCount > 0).isTrue() ); } diff --git a/server/src/intTest/java/com/ticketing/server/user/application/AuthControllerTest.java b/server/src/intTest/java/com/ticketing/server/user/application/AuthControllerTest.java index 856a3cc..18e6eca 100644 --- a/server/src/intTest/java/com/ticketing/server/user/application/AuthControllerTest.java +++ b/server/src/intTest/java/com/ticketing/server/user/application/AuthControllerTest.java @@ -1,5 +1,7 @@ package com.ticketing.server.user.application; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; @@ -9,8 +11,11 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.ticketing.server.global.redis.RefreshRedisRepository; import com.ticketing.server.user.application.request.LoginRequest; +import com.ticketing.server.user.application.request.RefreshRequest; import com.ticketing.server.user.application.request.SignUpRequest; +import com.ticketing.server.user.application.response.TokenResponse; import com.ticketing.server.user.service.interfaces.UserService; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -30,8 +35,12 @@ import org.springframework.web.context.WebApplicationContext; class AuthControllerTest { private static final String LOGIN_URL = "/api/auth/token"; + private static final String REFRESH_URL = "/api/auth/refresh"; + private static final String LOGOUT_URL = "/api/auth/logout"; private static final String REGISTER_URL = "/api/users"; + private static final String USER_EMAIL = "ticketing@gmail.com"; + private static final String USER_PW = "qwe123"; @Autowired WebApplicationContext context; @@ -54,7 +63,7 @@ class AuthControllerTest { @DisplayName("로그인 인증 성공") void loginSuccess() throws Exception { // given - LoginRequest request = new LoginRequest(USER_EMAIL, "qwe123"); + LoginRequest request = new LoginRequest(USER_EMAIL, USER_PW); // when ResultActions actions = mvc.perform(post(LOGIN_URL) @@ -82,6 +91,69 @@ class AuthControllerTest { .andExpect(status().isUnauthorized()); } + @Test + @DisplayName("리프레쉬 토큰 발급 성공") + void refreshTokenSuccess() throws Exception { + // given + LoginRequest loginRequest = new LoginRequest(USER_EMAIL, USER_PW); + + // when + // 로그인 + String loginResponseBody = mvc.perform(post(LOGIN_URL) + .content(asJsonString(loginRequest)) + .contentType(MediaType.APPLICATION_JSON)) + .andReturn() + .getResponse() + .getContentAsString(); + + TokenResponse loginResponse = objectMapper.readValue(loginResponseBody, TokenResponse.class); + RefreshRequest refreshRequest = new RefreshRequest(loginResponse.getRefreshToken()); + + // 토큰재발급 + String refreshResponseBody = mvc.perform(post(REFRESH_URL) + .content(asJsonString(refreshRequest)) + .contentType(MediaType.APPLICATION_JSON)) + .andReturn() + .getResponse() + .getContentAsString(); + + TokenResponse refreshBody = objectMapper.readValue(refreshResponseBody, TokenResponse.class); + + // then + assertAll( + () -> assertThat(refreshBody.getAccessToken()).isNotEmpty(), + () -> assertThat(refreshBody.getRefreshToken()).isNotEmpty(), + () -> assertThat(loginResponse.getTokenType()).isEqualTo(refreshBody.getTokenType()), + () -> assertThat(loginResponse.getExpiresIn()).isEqualTo(refreshBody.getExpiresIn()) + ); + } + + @Test + @DisplayName("로그아웃 성공") + void logoutSuccess() throws Exception { + // given + LoginRequest loginRequest = new LoginRequest(USER_EMAIL, USER_PW); + + // 로그인 + String loginResponseBody = mvc.perform(post(LOGIN_URL) + .content(asJsonString(loginRequest)) + .contentType(MediaType.APPLICATION_JSON)) + .andReturn() + .getResponse() + .getContentAsString(); + + TokenResponse loginResponse = objectMapper.readValue(loginResponseBody, TokenResponse.class); + String authorization = loginResponse.getTokenType() + " " + loginResponse.getAccessToken(); + + // 로그아웃 + ResultActions actions = mvc.perform(post(LOGOUT_URL) + .header("Authorization", authorization)); + + // then + actions.andDo(print()) + .andExpect(status().isOk()); + } + @BeforeEach void init() throws Exception { mvc = MockMvcBuilders @@ -89,7 +161,7 @@ class AuthControllerTest { .apply(springSecurity()) .build(); - SignUpRequest signUpRequest = new SignUpRequest("ticketing", USER_EMAIL, "qwe123", "010-1234-5678"); + SignUpRequest signUpRequest = new SignUpRequest("ticketing", USER_EMAIL, USER_PW, "010-1234-5678"); mvc.perform(post(REGISTER_URL) .content(asJsonString(signUpRequest)) @@ -102,6 +174,7 @@ class AuthControllerTest { } private String asJsonString(Object object) throws JsonProcessingException { + return objectMapper.writeValueAsString(object); } diff --git a/server/src/intTest/java/com/ticketing/server/user/application/UserControllerTest.java b/server/src/intTest/java/com/ticketing/server/user/application/UserControllerTest.java index f7c82f3..cf69021 100644 --- a/server/src/intTest/java/com/ticketing/server/user/application/UserControllerTest.java +++ b/server/src/intTest/java/com/ticketing/server/user/application/UserControllerTest.java @@ -69,7 +69,6 @@ class UserControllerTest { SignUpRequest signUpRequest; - @Test @DisplayName("회원가입 성공") void registerSuccess() throws Exception { diff --git a/server/src/main/java/com/ticketing/server/user/application/AuthController.java b/server/src/main/java/com/ticketing/server/user/application/AuthController.java index a41d5ab..dc088ea 100644 --- a/server/src/main/java/com/ticketing/server/user/application/AuthController.java +++ b/server/src/main/java/com/ticketing/server/user/application/AuthController.java @@ -1,6 +1,7 @@ package com.ticketing.server.user.application; import com.ticketing.server.user.application.request.LoginRequest; +import com.ticketing.server.user.application.request.RefreshRequest; import com.ticketing.server.user.application.response.LogoutResponse; import com.ticketing.server.user.service.dto.TokenDTO; import com.ticketing.server.user.application.response.TokenResponse; @@ -37,8 +38,8 @@ public class AuthController { } @PostMapping("/refresh") - public ResponseEntity refreshToken(@RequestParam("refreshToken") String refreshToken) { - TokenDTO tokenDto = authenticationService.reissueTokenDto(refreshToken); + public ResponseEntity refreshToken(@RequestBody RefreshRequest request) { + TokenDTO tokenDto = authenticationService.reissueTokenDto(request.getRefreshToken()); return ResponseEntity.status(HttpStatus.OK) .headers(getHttpHeaders()) diff --git a/server/src/main/java/com/ticketing/server/user/application/request/RefreshRequest.java b/server/src/main/java/com/ticketing/server/user/application/request/RefreshRequest.java new file mode 100644 index 0000000..ba8065f --- /dev/null +++ b/server/src/main/java/com/ticketing/server/user/application/request/RefreshRequest.java @@ -0,0 +1,14 @@ +package com.ticketing.server.user.application.request; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class RefreshRequest { + + private String refreshToken; + +} diff --git a/server/src/main/java/com/ticketing/server/user/application/response/TokenResponse.java b/server/src/main/java/com/ticketing/server/user/application/response/TokenResponse.java index 0921967..041ec99 100644 --- a/server/src/main/java/com/ticketing/server/user/application/response/TokenResponse.java +++ b/server/src/main/java/com/ticketing/server/user/application/response/TokenResponse.java @@ -2,14 +2,16 @@ package com.ticketing.server.user.application.response; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; @Getter +@NoArgsConstructor @AllArgsConstructor public class TokenResponse { - private final String accessToken; - private final String refreshToken; - private final String tokenType; - private final long expiresIn; + private String accessToken; + private String refreshToken; + private String tokenType; + private long expiresIn; } diff --git a/server/src/main/java/com/ticketing/server/user/service/AuthenticationServiceImpl.java b/server/src/main/java/com/ticketing/server/user/service/AuthenticationServiceImpl.java index ef29af0..60fd8fe 100644 --- a/server/src/main/java/com/ticketing/server/user/service/AuthenticationServiceImpl.java +++ b/server/src/main/java/com/ticketing/server/user/service/AuthenticationServiceImpl.java @@ -3,7 +3,6 @@ package com.ticketing.server.user.service; import com.ticketing.server.global.exception.ErrorCode; import com.ticketing.server.global.redis.RefreshRedisRepository; import com.ticketing.server.global.redis.RefreshToken; -import com.ticketing.server.global.security.jwt.JwtProperties; import com.ticketing.server.global.security.jwt.JwtProvider; import com.ticketing.server.user.service.dto.DeleteRefreshTokenDTO; import com.ticketing.server.user.service.dto.TokenDTO; @@ -14,7 +13,6 @@ import org.springframework.security.config.annotation.authentication.builders.Au import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.StringUtils; @Service @RequiredArgsConstructor @@ -23,7 +21,6 @@ public class AuthenticationServiceImpl implements AuthenticationService { private final RefreshRedisRepository refreshRedisRepository; private final JwtProvider jwtProvider; - private final JwtProperties jwtProperties; private final AuthenticationManagerBuilder authenticationManagerBuilder; @Override @@ -55,9 +52,7 @@ public class AuthenticationServiceImpl implements AuthenticationService { @Override @Transactional - public TokenDTO reissueTokenDto(String bearerRefreshToken) { - String refreshToken = resolveToken(bearerRefreshToken); - + public TokenDTO reissueTokenDto(String refreshToken) { // 토큰 검증 jwtProvider.validateToken(refreshToken); @@ -67,7 +62,7 @@ public class AuthenticationServiceImpl implements AuthenticationService { RefreshToken findTokenEntity = refreshRedisRepository.findByEmail(authentication.getName()) .orElseThrow(ErrorCode::throwRefreshTokenNotFound); - // redis 토큰과 input 토큰이 일치한지 확인 + // input 토큰이 최신 토큰이 아닐 경우 예외 처리 if (!refreshToken.equals(findTokenEntity.getToken())) { throw ErrorCode.throwUnavailableRefreshToken(); } @@ -94,11 +89,4 @@ public class AuthenticationServiceImpl implements AuthenticationService { ); } - private String resolveToken(String bearerToken) { - if (StringUtils.hasText(bearerToken) && jwtProperties.hasTokenStartsWith(bearerToken)) { - return bearerToken.substring(7); - } - throw ErrorCode.throwTokenType(); - } - } diff --git a/server/src/test/java/com/ticketing/server/user/service/AuthenticationServiceImplTest.java b/server/src/test/java/com/ticketing/server/user/service/AuthenticationServiceImplTest.java index c6f6015..fd1a3bd 100644 --- a/server/src/test/java/com/ticketing/server/user/service/AuthenticationServiceImplTest.java +++ b/server/src/test/java/com/ticketing/server/user/service/AuthenticationServiceImplTest.java @@ -10,8 +10,8 @@ import com.ticketing.server.global.redis.RefreshRedisRepository; import com.ticketing.server.global.redis.RefreshToken; import com.ticketing.server.global.security.jwt.JwtProperties; import com.ticketing.server.global.security.jwt.JwtProvider; -import com.ticketing.server.user.service.dto.TokenDTO; import com.ticketing.server.user.domain.UserGrade; +import com.ticketing.server.user.service.dto.TokenDTO; import java.util.Collections; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; @@ -62,7 +62,7 @@ class AuthenticationServiceImplTest { @DisplayName("토큰 재발급 성공") void reissueAccessToken() { // given - String refreshToken = "Bearer eyJhbGciOiJIUzUxMiJ9"; + String refreshToken = "eyJhbGciOiJIUzUxMiJ9"; when(jwtProvider.validateToken(any())).thenReturn(true); when(jwtProvider.getAuthentication(any())).thenReturn(authenticationToken); when(jwtProvider.generateTokenDto(any())).thenReturn(useJwtProvider.generateTokenDto(authenticationToken));