Skip to content

Commit

Permalink
#17 [Update] Access Token 재발급
Browse files Browse the repository at this point in the history
  • Loading branch information
Anna-Jin committed Jul 13, 2022
1 parent 5ab20a8 commit c7ca25e
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 34 deletions.
30 changes: 16 additions & 14 deletions src/main/java/com/mpnp/baechelin/oauth/common/AuthResponse.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mpnp.baechelin.oauth.common;

import com.mpnp.baechelin.oauth.exception.ErrorCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

Expand All @@ -9,15 +10,6 @@
@Getter
@RequiredArgsConstructor
public class AuthResponse<T> {
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<String, T> body;
Expand All @@ -26,22 +18,32 @@ public static <T> AuthResponse<T> success(String name, T body) {
Map<String, T> 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 <T> AuthResponse<T> 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 <T> AuthResponse<T> 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 <T> AuthResponse<T> 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 <T> AuthResponse<T> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
10 changes: 9 additions & 1 deletion src/main/java/com/mpnp/baechelin/oauth/exception/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
27 changes: 25 additions & 2 deletions src/main/java/com/mpnp/baechelin/oauth/token/AuthToken.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -51,7 +52,6 @@ public boolean validate() {
return this.getTokenClaims() != null;
}


// 토큰의 claims, payload 값 가져오기
public Claims getTokenClaims() {
try {
Expand All @@ -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 토큰입니다.");
Expand All @@ -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() {
Expand Down

0 comments on commit c7ca25e

Please sign in to comment.