From f7d80fce7940a424f3eae5791896b283ab0b0fb1 Mon Sep 17 00:00:00 2001 From: Kwak Seong Joon Date: Mon, 27 May 2024 16:54:02 +0900 Subject: [PATCH] =?UTF-8?q?[Feat]=20Jwt=20=ED=86=A0=ED=81=B0=20=EB=B0=9C?= =?UTF-8?q?=EA=B8=89=20=EB=B6=84=EB=A6=AC(#13)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/dto/ErrorMessage.java | 16 ++++++- .../common/jwt/JwtTokenProvider.java | 13 +++--- .../common/jwt/JwtTokenValidator.java | 43 +++++++++++++------ .../common/jwt/JwtValidationType.java | 20 ++++----- 4 files changed, 60 insertions(+), 32 deletions(-) diff --git a/springFirstSeminar/src/main/java/org/sopt/springFirstSeminar/common/dto/ErrorMessage.java b/springFirstSeminar/src/main/java/org/sopt/springFirstSeminar/common/dto/ErrorMessage.java index 966fae2..263f9d2 100644 --- a/springFirstSeminar/src/main/java/org/sopt/springFirstSeminar/common/dto/ErrorMessage.java +++ b/springFirstSeminar/src/main/java/org/sopt/springFirstSeminar/common/dto/ErrorMessage.java @@ -13,7 +13,21 @@ public enum ErrorMessage { BLOG_NOT_MATCH_MEMBER(HttpStatus.NOT_FOUND.value(), "해당 멤버 ID에 해당하는 블로그ID가 아닙니다."), CONTENT_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "POST ID에 해당하는 글이 존재하지 않습니다"), MAX_BLOG_CONTENT(HttpStatus.BAD_REQUEST.value(), "블로그 글이 최대 글자 수(20)를 초과했습니다"), - JWT_UNAUTHORIZED_EXCEPTION(HttpStatus.UNAUTHORIZED.value(), "사용자의 로그인 검증을 실패했습니다.") + + //jwt + JWT_UNAUTHORIZED_EXCEPTION(HttpStatus.UNAUTHORIZED.value(), "사용자의 로그인 검증을 실패했습니다."), + INVALID_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED.value(), "액세스 토큰의 형식이 올바르지 않습니다. Bearer 타입을 확인해 주세요."), + EXPIRED_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED.value(), "액세스 토큰이 만료되었습니다. 재발급 받아주세요."), + UNSUPPORTED_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED.value(), "지원하지 않는 JWT 형식입니다."), + EMPTY_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED.value(), "JWT Claim이 비어있습니다"), + JWT_SIGNATURE_EXCEPTION(HttpStatus.UNAUTHORIZED.value(), "JWT의 기존 서명과 다릅니다."), + + INVALID_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED.value(), "리프레시 토큰의 형식이 올바르지 않습니다."), + INVALID_REFRESH_TOKEN_VALUE(HttpStatus.UNAUTHORIZED.value(), "리프레시 토큰의 값이 올바르지 않습니다."), + EXPIRED_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED.value(),"리프레시 토큰이 만료되었습니다. 다시 로그인해 주세요."), + MISMATCH_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED.value(), "리프레시 토큰이 일치하지 않습니다."), + + ; private final int status; diff --git a/springFirstSeminar/src/main/java/org/sopt/springFirstSeminar/common/jwt/JwtTokenProvider.java b/springFirstSeminar/src/main/java/org/sopt/springFirstSeminar/common/jwt/JwtTokenProvider.java index bd80632..79cd7b9 100644 --- a/springFirstSeminar/src/main/java/org/sopt/springFirstSeminar/common/jwt/JwtTokenProvider.java +++ b/springFirstSeminar/src/main/java/org/sopt/springFirstSeminar/common/jwt/JwtTokenProvider.java @@ -1,6 +1,7 @@ package org.sopt.springFirstSeminar.common.jwt; import io.jsonwebtoken.Claims; +import io.jsonwebtoken.JwtParser; import lombok.RequiredArgsConstructor; import org.sopt.springFirstSeminar.common.jwt.dto.Token; import org.sopt.springFirstSeminar.common.jwt.dto.TokenResponse; @@ -20,12 +21,10 @@ public Token issueTokens(final Long userId) { jwtTokenGenerator.generateToken(userId, false)); } - public Long getUserFromJwt(String token) { - Claims claims = getBody(token); - return Long.valueOf(claims.get(USER_ID).toString()); + public Long getSubject(String accessToken) { + JwtParser jwtParser = jwtTokenGenerator.getJwtParser(); + return Long.valueOf(jwtParser.parseClaimsJws(accessToken) + .getBody() + .getSubject()); } - - - - } diff --git a/springFirstSeminar/src/main/java/org/sopt/springFirstSeminar/common/jwt/JwtTokenValidator.java b/springFirstSeminar/src/main/java/org/sopt/springFirstSeminar/common/jwt/JwtTokenValidator.java index d5bc964..bc7febb 100644 --- a/springFirstSeminar/src/main/java/org/sopt/springFirstSeminar/common/jwt/JwtTokenValidator.java +++ b/springFirstSeminar/src/main/java/org/sopt/springFirstSeminar/common/jwt/JwtTokenValidator.java @@ -2,6 +2,8 @@ import io.jsonwebtoken.*; import lombok.RequiredArgsConstructor; +import org.sopt.springFirstSeminar.common.dto.ErrorMessage; +import org.sopt.springFirstSeminar.exception.UnauthorizedException; import org.springframework.stereotype.Component; @@ -11,27 +13,40 @@ public class JwtTokenValidator { private final JwtTokenGenerator jwtTokenGenerator; - public JwtValidationType validateToken(String token) { + public void validateAccessToken(String accessToken) { try { - final Claims claims = getBody(token); - return JwtValidationType.VALID_JWT; + parseToken(accessToken); + } catch (ExpiredJwtException e) { + throw new UnauthorizedException(ErrorMessage.EXPIRED_ACCESS_TOKEN); } catch (MalformedJwtException ex) { - return JwtValidationType.INVALID_JWT_TOKEN; - } catch (ExpiredJwtException ex) { - return JwtValidationType.EXPIRED_JWT_TOKEN; + throw new UnauthorizedException(ErrorMessage.INVALID_ACCESS_TOKEN); } catch (UnsupportedJwtException ex) { - return JwtValidationType.UNSUPPORTED_JWT_TOKEN; + throw new UnauthorizedException(ErrorMessage.UNSUPPORTED_ACCESS_TOKEN); } catch (IllegalArgumentException ex) { - return JwtValidationType.EMPTY_JWT; + throw new UnauthorizedException(ErrorMessage.EMPTY_ACCESS_TOKEN); + } catch (SignatureException ex) { + throw new UnauthorizedException(ErrorMessage.JWT_SIGNATURE_EXCEPTION); } } - private Claims getBody(final String token) { - return Jwts.parserBuilder() - .setSigningKey(jwtTokenGenerator.getSigningKey()) - .build() - .parseClaimsJws(token) - .getBody(); + public void validateRefreshToken(String refreshToken) { + try { + parseToken(refreshToken); + } catch (ExpiredJwtException e) { + throw new UnauthorizedException(ErrorMessage.EXPIRED_REFRESH_TOKEN); + } catch (Exception e) { + throw new UnauthorizedException(ErrorMessage.INVALID_REFRESH_TOKEN_VALUE); + } + } + + public void equalsRefreshToken(String refreshToken, String storedRefreshToken) { + if (!refreshToken.equals(storedRefreshToken)) { + throw new UnauthorizedException(ErrorMessage.MISMATCH_REFRESH_TOKEN); + } } + private void parseToken(String token) { + JwtParser jwtParser = jwtTokenGenerator.getJwtParser(); + jwtParser.parseClaimsJws(token); + } } diff --git a/springFirstSeminar/src/main/java/org/sopt/springFirstSeminar/common/jwt/JwtValidationType.java b/springFirstSeminar/src/main/java/org/sopt/springFirstSeminar/common/jwt/JwtValidationType.java index e2b98b4..f67a7b9 100644 --- a/springFirstSeminar/src/main/java/org/sopt/springFirstSeminar/common/jwt/JwtValidationType.java +++ b/springFirstSeminar/src/main/java/org/sopt/springFirstSeminar/common/jwt/JwtValidationType.java @@ -1,10 +1,10 @@ -package org.sopt.springFirstSeminar.common.jwt; - -public enum JwtValidationType { - VALID_JWT, // 유효한 JWT - INVALID_JWT_SIGNATURE, // 유효하지 않은 서명 - INVALID_JWT_TOKEN, // 유효하지 않은 토큰 - EXPIRED_JWT_TOKEN, // 만료된 토큰 - UNSUPPORTED_JWT_TOKEN, // 지원하지 않는 형식의 토큰 - EMPTY_JWT // 빈 JWT -} +//package org.sopt.springFirstSeminar.common.jwt; +// +//public enum JwtValidationType { +// VALID_JWT, // 유효한 JWT +// INVALID_JWT_SIGNATURE, // 유효하지 않은 서명 +// INVALID_JWT_TOKEN, // 유효하지 않은 토큰 +// EXPIRED_JWT_TOKEN, // 만료된 토큰 +// UNSUPPORTED_JWT_TOKEN, // 지원하지 않는 형식의 토큰 +// EMPTY_JWT // 빈 JWT +//}