From c7ca25e549ef925de1410185c44ccebc09c734e1 Mon Sep 17 00:00:00 2001 From: Anna-Jin Date: Wed, 13 Jul 2022 14:45:39 +0900 Subject: [PATCH] =?UTF-8?q?#17=20[Update]=20Access=20Token=20=EC=9E=AC?= =?UTF-8?q?=EB=B0=9C=EA=B8=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../baechelin/oauth/common/AuthResponse.java | 30 ++++++------- .../oauth/common/AuthResponseHeader.java | 1 + .../oauth/controller/AuthController.java | 42 +++++++++++++++++++ .../baechelin/oauth/exception/ErrorCode.java | 10 ++++- .../OAuth2AuthenticationSuccessHandler.java | 2 + .../AuthService.java} | 34 +++++++-------- .../mpnp/baechelin/oauth/token/AuthToken.java | 27 +++++++++++- 7 files changed, 112 insertions(+), 34 deletions(-) create mode 100644 src/main/java/com/mpnp/baechelin/oauth/controller/AuthController.java rename src/main/java/com/mpnp/baechelin/oauth/{controller/authController.java => service/AuthService.java} (84%) diff --git a/src/main/java/com/mpnp/baechelin/oauth/common/AuthResponse.java b/src/main/java/com/mpnp/baechelin/oauth/common/AuthResponse.java index a7d08d8..78979ca 100644 --- a/src/main/java/com/mpnp/baechelin/oauth/common/AuthResponse.java +++ b/src/main/java/com/mpnp/baechelin/oauth/common/AuthResponse.java @@ -1,5 +1,6 @@ package com.mpnp.baechelin.oauth.common; +import com.mpnp.baechelin.oauth.exception.ErrorCode; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -9,15 +10,6 @@ @Getter @RequiredArgsConstructor public class AuthResponse { - private final static int SUCCESS = 200; - private final static int NOT_FOUND = 400; - private final static int FAILED = 500; - private final static String SUCCESS_MESSAGE = "SUCCESS"; - private final static String NOT_FOUND_MESSAGE = "NOT FOUND"; - private final static String FAILED_MESSAGE = "서버에서 오류가 발생하였습니다."; - private final static String INVALID_ACCESS_TOKEN = "Invalid access token."; - private final static String INVALID_REFRESH_TOKEN = "Invalid refresh token."; - private final static String NOT_EXPIRED_TOKEN_YET = "Not expired token yet."; private final AuthResponseHeader header; private final Map body; @@ -26,22 +18,32 @@ public static AuthResponse success(String name, T body) { Map map = new HashMap<>(); map.put(name, body); - return new AuthResponse<>(new AuthResponseHeader(SUCCESS, SUCCESS_MESSAGE), map); + return new AuthResponse<>(new AuthResponseHeader( + ErrorCode.SUCCESS_MESSAGE.getCode(), + ErrorCode.SUCCESS_MESSAGE.getMessage()), map); } public static AuthResponse fail() { - return new AuthResponse<>(new AuthResponseHeader(FAILED, FAILED_MESSAGE), null); + return new AuthResponse<>(new AuthResponseHeader( + ErrorCode.FAILED_MESSAGE.getCode(), + ErrorCode.FAILED_MESSAGE.getMessage()), null); } public static AuthResponse invalidAccessToken() { - return new AuthResponse<>(new AuthResponseHeader(FAILED, INVALID_ACCESS_TOKEN), null); + return new AuthResponse<>(new AuthResponseHeader( + ErrorCode.INVALID_ACCESS_TOKEN.getCode(), + ErrorCode.INVALID_ACCESS_TOKEN.getMessage()), null); } public static AuthResponse invalidRefreshToken() { - return new AuthResponse<>(new AuthResponseHeader(FAILED, INVALID_REFRESH_TOKEN), null); + return new AuthResponse<>(new AuthResponseHeader( + ErrorCode.INVALID_REFRESH_TOKEN.getCode(), + ErrorCode.INVALID_REFRESH_TOKEN.getMessage()), null); } public static AuthResponse notExpiredTokenYet() { - return new AuthResponse<>(new AuthResponseHeader(FAILED, NOT_EXPIRED_TOKEN_YET), null); + return new AuthResponse<>(new AuthResponseHeader( + ErrorCode.NOT_EXPIRED_TOKEN_YET.getCode(), + ErrorCode.NOT_EXPIRED_TOKEN_YET.getMessage()), null); } } diff --git a/src/main/java/com/mpnp/baechelin/oauth/common/AuthResponseHeader.java b/src/main/java/com/mpnp/baechelin/oauth/common/AuthResponseHeader.java index a64a9ba..6af1325 100644 --- a/src/main/java/com/mpnp/baechelin/oauth/common/AuthResponseHeader.java +++ b/src/main/java/com/mpnp/baechelin/oauth/common/AuthResponseHeader.java @@ -1,5 +1,6 @@ package com.mpnp.baechelin.oauth.common; +import com.mpnp.baechelin.oauth.exception.ErrorCode; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/com/mpnp/baechelin/oauth/controller/AuthController.java b/src/main/java/com/mpnp/baechelin/oauth/controller/AuthController.java new file mode 100644 index 0000000..ee9b4da --- /dev/null +++ b/src/main/java/com/mpnp/baechelin/oauth/controller/AuthController.java @@ -0,0 +1,42 @@ +package com.mpnp.baechelin.oauth.controller; + +import com.mpnp.baechelin.config.properties.AppProperties; +import com.mpnp.baechelin.oauth.common.AuthResponse; +import com.mpnp.baechelin.oauth.entity.RoleType; +import com.mpnp.baechelin.oauth.service.AuthService; +import com.mpnp.baechelin.oauth.token.AuthToken; +import com.mpnp.baechelin.oauth.token.AuthTokenProvider; +import com.mpnp.baechelin.user.entity.user.UserRefreshToken; +import com.mpnp.baechelin.user.repository.UserRefreshTokenRepository; +import com.mpnp.baechelin.util.CookieUtil; +import com.mpnp.baechelin.util.HeaderUtil; +import io.jsonwebtoken.Claims; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.transaction.Transactional; +import java.util.Date; + +@RestController +@RequestMapping("/auth") +@RequiredArgsConstructor +public class AuthController { + + private final AuthService authService; + + /** + * access token 만료시 refresh 토큰 요청 + * @param request + * @param response + * @return + */ + @GetMapping("/refresh") + public AuthResponse refreshToken (HttpServletRequest request, HttpServletResponse response) { + return authService.refreshToken(request, response); + } +} diff --git a/src/main/java/com/mpnp/baechelin/oauth/exception/ErrorCode.java b/src/main/java/com/mpnp/baechelin/oauth/exception/ErrorCode.java index b50661a..1c636dc 100644 --- a/src/main/java/com/mpnp/baechelin/oauth/exception/ErrorCode.java +++ b/src/main/java/com/mpnp/baechelin/oauth/exception/ErrorCode.java @@ -6,7 +6,15 @@ @Getter @RequiredArgsConstructor public enum ErrorCode { - ALREADY_LOGIN_ACCOUNT("ALREADY_LOGIN_ACCOUNT"); + SUCCESS_MESSAGE(200, "SUCCESS"), + NOT_FOUND_MESSAGE(500, "NOT FOUND"), + FAILED_MESSAGE(500, "서버에서 오류가 발생하였습니다."), + INVALID_ACCESS_TOKEN(400, "Invalid access token."), + INVALID_REFRESH_TOKEN(400, "Invalid refresh token."), + NOT_EXPIRED_TOKEN_YET(400,"Not expired token yet."), + ALREADY_LOGIN_ACCOUNT(400, "ALREADY_LOGIN_ACCOUNT"); + + private final int code; private final String message; } diff --git a/src/main/java/com/mpnp/baechelin/oauth/handler/OAuth2AuthenticationSuccessHandler.java b/src/main/java/com/mpnp/baechelin/oauth/handler/OAuth2AuthenticationSuccessHandler.java index f7efcbd..6933d09 100644 --- a/src/main/java/com/mpnp/baechelin/oauth/handler/OAuth2AuthenticationSuccessHandler.java +++ b/src/main/java/com/mpnp/baechelin/oauth/handler/OAuth2AuthenticationSuccessHandler.java @@ -23,7 +23,9 @@ import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.transaction.Transactional; import java.io.IOException; +import java.lang.annotation.Documented; import java.net.URI; import java.util.Collection; import java.util.Date; diff --git a/src/main/java/com/mpnp/baechelin/oauth/controller/authController.java b/src/main/java/com/mpnp/baechelin/oauth/service/AuthService.java similarity index 84% rename from src/main/java/com/mpnp/baechelin/oauth/controller/authController.java rename to src/main/java/com/mpnp/baechelin/oauth/service/AuthService.java index 59ea1f1..c784e5d 100644 --- a/src/main/java/com/mpnp/baechelin/oauth/controller/authController.java +++ b/src/main/java/com/mpnp/baechelin/oauth/service/AuthService.java @@ -1,4 +1,4 @@ -package com.mpnp.baechelin.oauth.controller; +package com.mpnp.baechelin.oauth.service; import com.mpnp.baechelin.config.properties.AppProperties; import com.mpnp.baechelin.oauth.common.AuthResponse; @@ -10,40 +10,39 @@ import com.mpnp.baechelin.util.CookieUtil; import com.mpnp.baechelin.util.HeaderUtil; import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.MalformedJwtException; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Date; -@RestController -@RequestMapping("/auth") +@Slf4j +@Service @RequiredArgsConstructor -public class authController { +@Transactional +public class AuthService { private final AppProperties appProperties; private final AuthTokenProvider tokenProvider; private final UserRefreshTokenRepository userRefreshTokenRepository; - private final static long THREE_DAYS_MSEC = 259200000; private final static String REFRESH_TOKEN = "refresh_token"; - - /** - * access token 만료시 refresh 토큰 요청 - * @param request - * @param response - * @return - */ - @GetMapping("/refresh") - public AuthResponse refreshToken (HttpServletRequest request, HttpServletResponse response) { + public AuthResponse refreshToken(HttpServletRequest request, HttpServletResponse response) { String accessToken = HeaderUtil.getAccessToken(request); AuthToken authToken = tokenProvider.convertAuthToken(accessToken); + // 유효한 access token 인지 확인 + if (authToken.getTokenClaimsForRefresh() == null) { + return AuthResponse.invalidAccessToken(); + } + // expired access token 인지 확인 Claims claims = authToken.getExpiredTokenClaims(); if (claims == null) { @@ -91,6 +90,7 @@ public AuthResponse refreshToken (HttpServletRequest request, HttpServletRespons // DB에 refresh 토큰 업데이트 userRefreshToken.setRefreshToken(authRefreshToken.getToken()); + int cookieMaxAge = (int) refreshTokenExpiry / 60; CookieUtil.deleteCookie(request, response, REFRESH_TOKEN); CookieUtil.addCookie(response, REFRESH_TOKEN, authRefreshToken.getToken(), cookieMaxAge); diff --git a/src/main/java/com/mpnp/baechelin/oauth/token/AuthToken.java b/src/main/java/com/mpnp/baechelin/oauth/token/AuthToken.java index 00ee5d5..3869fa0 100644 --- a/src/main/java/com/mpnp/baechelin/oauth/token/AuthToken.java +++ b/src/main/java/com/mpnp/baechelin/oauth/token/AuthToken.java @@ -1,6 +1,7 @@ package com.mpnp.baechelin.oauth.token; import io.jsonwebtoken.*; +import io.jsonwebtoken.security.SignatureException; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -51,7 +52,6 @@ public boolean validate() { return this.getTokenClaims() != null; } - // 토큰의 claims, payload 값 가져오기 public Claims getTokenClaims() { try { @@ -60,7 +60,7 @@ public Claims getTokenClaims() { .build() .parseClaimsJws(token) .getBody(); - } catch (SecurityException e) { + } catch (SignatureException e) { log.info("잘못된 JWT 서명입니다."); } catch (MalformedJwtException e) { log.info("유효하지 않은 구성의 JWT 토큰입니다."); @@ -74,6 +74,29 @@ public Claims getTokenClaims() { return null; } + // Access token을 재발급 받을 때 token이 유효한지 검사하는 로직 + public Claims getTokenClaimsForRefresh() { + try { + return Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token) + .getBody(); + } catch (SecurityException e) { + log.info("잘못된 JWT 서명입니다."); + } catch (MalformedJwtException e) { + log.info("유효하지 않은 구성의 JWT 토큰입니다."); + } catch (UnsupportedJwtException e) { + log.info("지원되지 않는 형식이나 구성의 JWT 토큰입니다."); + } catch (IllegalArgumentException e) { + log.info(e.toString().split(":")[1].trim()); + } catch (ExpiredJwtException e) { + log.info("만료된 JWT 토큰입니다."); + return e.getClaims(); + } + return null; + } + // 만료된 토큰인지 확인하는 용도 public Claims getExpiredTokenClaims() {