Skip to content

Commit

Permalink
[BE] 토큰 확인하는 API를 구현한다. (#767)
Browse files Browse the repository at this point in the history
  • Loading branch information
JINU-CHANG authored and tkdgur0906 committed Oct 15, 2024
1 parent fd4e2d7 commit 94eb3d2
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public User resolveArgument(MethodParameter parameter, ModelAndViewContainer mav
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();

cookieResolver.checkLoginRequired(request);
String token = cookieResolver.extractAccessToken(request.getCookies());
String token = cookieResolver.extractAccessToken(request);
return authService.getAuthUser(token);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m
return authService.assignGuestUser();
}

String token = cookieResolver.extractAccessToken(request.getCookies());
String token = cookieResolver.extractAccessToken(request);
return authService.getAuthUser(token);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
import com.bang_ggood.auth.controller.cookie.CookieResolver;
import com.bang_ggood.auth.dto.request.OauthLoginRequest;
import com.bang_ggood.auth.dto.response.AuthTokenResponse;
import com.bang_ggood.auth.dto.response.RefreshTokenCheckResponse;
import com.bang_ggood.auth.service.AuthService;
import com.bang_ggood.user.domain.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
Expand Down Expand Up @@ -45,8 +47,8 @@ public ResponseEntity<Void> login(@Valid @RequestBody OauthLoginRequest request)
@PostMapping("/oauth/logout")
public ResponseEntity<Void> logout(@AuthRequiredPrincipal User user,
HttpServletRequest httpServletRequest) {
String accessToken = cookieResolver.extractAccessToken(httpServletRequest.getCookies());
String refreshToken = cookieResolver.extractRefreshToken(httpServletRequest.getCookies());
String accessToken = cookieResolver.extractAccessToken(httpServletRequest);
String refreshToken = cookieResolver.extractRefreshToken(httpServletRequest);

authService.logout(accessToken, refreshToken, user);

Expand All @@ -60,15 +62,23 @@ public ResponseEntity<Void> logout(@AuthRequiredPrincipal User user,
}

@PostMapping("/accessToken/reissue")
public ResponseEntity<Void> reIssueAccessToken(HttpServletRequest httpServletRequest) {
public ResponseEntity<Void> reissueAccessToken(HttpServletRequest httpServletRequest) {
cookieResolver.checkLoginRequired(httpServletRequest);

String refreshToken = cookieResolver.extractRefreshToken(httpServletRequest.getCookies());
String accessToken = authService.reIssueAccessToken(refreshToken);
String refreshToken = cookieResolver.extractRefreshToken(httpServletRequest);
String accessToken = authService.reissueAccessToken(refreshToken);

ResponseCookie accessTokenCookie = cookieProvider.createAccessTokenCookie(accessToken);
return ResponseEntity.ok()
.header(HttpHeaders.SET_COOKIE, accessTokenCookie.toString())
.build();
}

@GetMapping("/refreshToken-check")
public ResponseEntity<RefreshTokenCheckResponse> check(HttpServletRequest httpServletRequest) {
boolean isRefreshTokenExist = !cookieResolver.isRefreshTokenEmpty(httpServletRequest);

RefreshTokenCheckResponse refreshTokenCheckResponse = RefreshTokenCheckResponse.from(isRefreshTokenExist);
return ResponseEntity.ok(refreshTokenCheckResponse);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,47 +16,50 @@ public void checkLoginRequired(HttpServletRequest request) {
throw new BangggoodException(ExceptionCode.AUTHENTICATION_TOKEN_EMPTY);
}

if (isRefreshTokenEmpty(request.getCookies())) {
if (isRefreshTokenEmpty(request)) {
throw new BangggoodException(ExceptionCode.AUTHENTICATION_REFRESH_TOKEN_EMPTY);
}
}

private boolean isAccessTokenEmpty(Cookie[] cookies) {
return isTokenEmpty(cookies, CookieProvider.ACCESS_TOKEN_COOKIE_NAME);
private boolean isAccessTokenEmpty(HttpServletRequest request) {
return isTokenEmpty(request, CookieProvider.ACCESS_TOKEN_COOKIE_NAME);
}

private boolean isRefreshTokenEmpty(Cookie[] cookies) {
return isTokenEmpty(cookies, CookieProvider.REFRESH_TOKEN_COOKIE_NAME);
public boolean isRefreshTokenEmpty(HttpServletRequest request) {
return isTokenEmpty(request, CookieProvider.REFRESH_TOKEN_COOKIE_NAME);
}

private boolean isTokenEmpty(Cookie[] cookies, String cookieName) {
return Arrays.stream(cookies)
private boolean isTokenEmpty(HttpServletRequest request, String cookieName) {
if (request.getCookies() == null) {
return true;
}

return Arrays.stream(request.getCookies())
.noneMatch(cookie -> cookie.getName().equals(cookieName));
}

public String extractAccessToken(Cookie[] cookies) {
return extractToken(cookies, CookieProvider.ACCESS_TOKEN_COOKIE_NAME)
public String extractAccessToken(HttpServletRequest request) {
return extractToken(request, CookieProvider.ACCESS_TOKEN_COOKIE_NAME)
.orElseThrow(() -> new BangggoodException(ExceptionCode.AUTHENTICATION_ACCESS_TOKEN_EMPTY));
}

public String extractRefreshToken(Cookie[] cookies) {
return extractToken(cookies, CookieProvider.REFRESH_TOKEN_COOKIE_NAME)
public String extractRefreshToken(HttpServletRequest request) {
return extractToken(request, CookieProvider.REFRESH_TOKEN_COOKIE_NAME)
.orElseThrow(() -> new BangggoodException(ExceptionCode.AUTHENTICATION_REFRESH_TOKEN_EMPTY));
}

private Optional<String> extractToken(Cookie[] cookies, String cookieName) {
return Arrays.stream(cookies)
private Optional<String> extractToken(HttpServletRequest request, String cookieName) {
if (isTokenEmpty(request)) {
return Optional.empty();
}

return Arrays.stream(request.getCookies())
.filter(cookie -> cookie.getName().equals(cookieName))
.findAny()
.map(Cookie::getValue);
}

public boolean isTokenEmpty(HttpServletRequest request) {
if (request.getCookies() == null) {
return true;
}

Cookie[] cookies = request.getCookies();
return isAccessTokenEmpty(cookies) && isRefreshTokenEmpty(cookies);
return isAccessTokenEmpty(request) && isRefreshTokenEmpty(request);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.bang_ggood.auth.dto.response;

public record RefreshTokenCheckResponse(boolean isRefreshTokenExist) {

public static RefreshTokenCheckResponse from(boolean isRefreshTokenExist) {
return new RefreshTokenCheckResponse(isRefreshTokenExist);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public User getAuthUser(String token) {
}

@Transactional(readOnly = true)
public String reIssueAccessToken(String refreshToken) {
public String reissueAccessToken(String refreshToken) {
AuthUser authUser = jwtTokenResolver.resolveRefreshToken(refreshToken);
User user = userRepository.getUserById(authUser.id());
return jwtTokenProvider.createAccessToken(user);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
import com.bang_ggood.station.domain.ChecklistStation;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;

public interface ChecklistStationRepository extends JpaRepository<ChecklistStation, Long> {

@Query("SELECT cs FROM ChecklistStation cs " +
"where cs.checklist = :checklist " +
"and cs.deleted = false")
List<ChecklistStation> findByChecklist(Checklist checklist);
List<ChecklistStation> findByChecklist(@Param("checklist") Checklist checklist);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.bang_ggood.auth.controller;

import com.bang_ggood.AcceptanceTest;
import com.bang_ggood.auth.dto.response.RefreshTokenCheckResponse;
import com.bang_ggood.auth.service.AuthService;
import com.bang_ggood.global.exception.ExceptionCode;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.http.Header;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -56,4 +58,33 @@ void authentication_invalid_cookie_exception() {
.statusCode(401)
.body("message", containsString(ExceptionCode.AUTHENTICATION_TOKEN_EMPTY.getMessage()));
}

@DisplayName("리프레시 토큰 체크 성공 : 쿠키가 존재하지 않는 경우")
@Test
void checkRefreshToken_returnFalse() {
RefreshTokenCheckResponse refreshTokenCheckResponse = RestAssured.given().log().all()
.contentType(ContentType.JSON)
.when().get("/refreshToken-check")
.then().log().all()
.statusCode(200)
.extract()
.as(RefreshTokenCheckResponse.class);

Assertions.assertThat(refreshTokenCheckResponse.isRefreshTokenExist()).isFalse();
}

@DisplayName("리프레시 토큰 체크 성공 : 리프레시 토큰이 존재하는 경우")
@Test
void checkRefreshToken_returnTrue() {
RefreshTokenCheckResponse refreshTokenCheckResponse = RestAssured.given().log().all()
.contentType(ContentType.JSON)
.headers(this.headers)
.when().get("/refreshToken-check")
.then().log().all()
.statusCode(200)
.extract()
.as(RefreshTokenCheckResponse.class);

Assertions.assertThat(refreshTokenCheckResponse.isRefreshTokenExist()).isTrue();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.bang_ggood.auth.controller.cookie;

import com.bang_ggood.auth.controller.cookie.CookieProvider;
import com.bang_ggood.auth.controller.cookie.CookieResolver;
import com.bang_ggood.global.exception.BangggoodException;
import com.bang_ggood.global.exception.ExceptionCode;
import jakarta.servlet.http.Cookie;
Expand All @@ -19,12 +17,14 @@ class CookieResolverTest {
@Test
void extractAccessToken() {
// given
HttpServletRequest request = mock(HttpServletRequest.class);
String expectedToken = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI2IiwiaWF0Ijox";
CookieResolver cookieResolver = new CookieResolver();
Cookie[] cookies = { new Cookie(CookieProvider.ACCESS_TOKEN_COOKIE_NAME, expectedToken) };

// when
String token = cookieResolver.extractAccessToken(cookies);
when(request.getCookies()).thenReturn(cookies);
String token = cookieResolver.extractAccessToken(request);

// then
Assertions.assertThat(token).isEqualTo(expectedToken);
Expand All @@ -34,12 +34,14 @@ void extractAccessToken() {
@Test
void extractRefreshToken() {
// given
HttpServletRequest request = mock(HttpServletRequest.class);
String expectedToken = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI2IiwiaWF0Ijox";
CookieResolver cookieResolver = new CookieResolver();
Cookie[] cookies = { new Cookie(CookieProvider.REFRESH_TOKEN_COOKIE_NAME, expectedToken) };

// when
String token = cookieResolver.extractRefreshToken(cookies);
when(request.getCookies()).thenReturn(cookies);
String token = cookieResolver.extractRefreshToken(request);

// then
Assertions.assertThat(token).isEqualTo(expectedToken);
Expand All @@ -49,12 +51,14 @@ void extractRefreshToken() {
@Test
void tokenValueNotExist() {
// given
HttpServletRequest request = mock(HttpServletRequest.class);
CookieResolver cookieResolver = new CookieResolver();
Cookie[] cookies = new Cookie[1];
cookies[0] = new Cookie("testName", "testValue");

// when & then
Assertions.assertThatThrownBy(() -> cookieResolver.extractAccessToken(cookies))
when(request.getCookies()).thenReturn(cookies);
Assertions.assertThatThrownBy(() -> cookieResolver.extractAccessToken(request))
.isInstanceOf(BangggoodException.class)
.hasMessage(ExceptionCode.AUTHENTICATION_ACCESS_TOKEN_EMPTY.getMessage());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class AuthServiceTest extends IntegrationTestSupport {
@Autowired
private JwtTokenProvider jwtTokenProvider;

@DisplayName("로그인 성공 : 존재하지 않는 회원이면 데이터베이스에 새로운 유저를 추가하고 토큰을 반환한다.")
@DisplayName("로그인 성공 : 존재하지 않는 회원이면 데이터베이스에 새로운 유저를 추가하고 토큰 반환")
@Test
void login_signup() {
// given
Expand All @@ -61,7 +61,7 @@ void login_signup() {
assertThat(token.refreshToken()).isNotBlank();
}

@DisplayName("로그인 성공 : 존재하는 회원이면 데이터베이스에 새로운 유저를 추가하지않고 토큰을 바로 반환한다.")
@DisplayName("로그인 성공 : 존재하는 회원이면 데이터베이스에 새로운 유저를 추가하지않고 토큰 반환")
@Test
void login() {
// given
Expand All @@ -77,7 +77,7 @@ void login() {
assertThat(token.refreshToken()).isNotBlank();
}

@DisplayName("로그인 성공 : 회원 가입시 디폴트 체크리스트 질문을 추가한다.")
@DisplayName("로그인 성공 : 회원 가입시 디폴트 체크리스트 질문 추가")
@Test
void login_default_checklist_question() {
// given
Expand All @@ -100,7 +100,7 @@ void login_default_checklist_question() {
assertThat(sum).isEqualTo(Question.findDefaultQuestions().size());
}

@DisplayName("로그인 성공 : 회원 가입시 디폴트 체크리스트를 추가한다.")
@DisplayName("로그인 성공 : 회원 가입시 디폴트 체크리스트 추가")
@Test
void login_default_checklist() {
// given
Expand All @@ -116,7 +116,7 @@ void login_default_checklist() {
assertThat(response.checklists()).hasSize(1);
}

@DisplayName("게스트 유저 할당 실패 : 게스트 유저의 수가 2명이면 예외를 발생시킨다.")
@DisplayName("게스트 유저 할당 실패 : 게스트 유저의 수가 2명이면 예외 발생")
@Test
void assignGuestUser_UnexpectedGuestUserExist() {
// given
Expand All @@ -129,7 +129,7 @@ void assignGuestUser_UnexpectedGuestUserExist() {
.hasMessage(ExceptionCode.GUEST_USER_UNEXPECTED_EXIST.getMessage());
}

@DisplayName("게스트 유저 할당 실패 : 게스트 유저가 존재하지 않으면 예외를 발생시킨다.")
@DisplayName("게스트 유저 할당 실패 : 게스트 유저가 존재하지 않으면 예외 발생")
@Test
void assingGuestUser_GuestUserNotExist() {
// when & then
Expand Down Expand Up @@ -169,15 +169,15 @@ void logout_invalid_ownership_exception() {

@DisplayName("액세스 토큰 재발행 성공")
@Test
void reIssueAccessToken() {
void reissueAccessToken() {
// given
userRepository.save(UserFixture.USER1());
Mockito.when(oauthClient.requestOauthInfo(any(OauthLoginRequest.class)))
.thenReturn(UserFixture.OAUTH_INFO_RESPONSE_USER1());
AuthTokenResponse tokenResponse = authService.login(OAUTH_LOGIN_REQUEST);

// when & then
assertThatCode(() -> authService.reIssueAccessToken(tokenResponse.refreshToken()))
assertThatCode(() -> authService.reissueAccessToken(tokenResponse.refreshToken()))
.doesNotThrowAnyException();

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ void createChecklist() {
void createChecklistV1() {
RestAssured.given().log().all()
.contentType(ContentType.JSON)
.header(new Header(HttpHeaders.COOKIE, this.responseCookie.toString()))
.headers(this.headers)
.body(ChecklistFixture.CHECKLIST_CREATE_REQUEST_V1())
.when().post("v1/checklists")
.then().log().all()
Expand Down Expand Up @@ -130,7 +130,7 @@ void readChecklistV1() {

RestAssured.given().log().all()
.contentType(ContentType.JSON)
.header(new Header(HttpHeaders.COOKIE, this.responseCookie.toString()))
.headers(this.headers)
.when().get("v1/checklists/" + checklistId)
.then().log().all()
.statusCode(200);
Expand Down

0 comments on commit 94eb3d2

Please sign in to comment.