From 3ce2d6fdd9f4711e7416810bc6eca73331ec14d7 Mon Sep 17 00:00:00 2001 From: cabbage16 Date: Thu, 10 Oct 2024 13:57:39 +0900 Subject: [PATCH 1/5] =?UTF-8?q?perf(#145):=20JWT=20=EC=84=9C=EB=AA=85=20?= =?UTF-8?q?=EC=95=8C=EA=B3=A0=EB=A6=AC=EC=A6=98=20=EB=AA=85=EC=8B=9C?= =?UTF-8?q?=EC=A0=81=20=EC=84=A0=EC=96=B8=20-=20JWT=EB=A1=9C=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EC=8B=9C=20=EC=84=9C=EB=AA=85=20=EC=95=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=EC=A6=98=EC=9D=B4=20HS256=EC=9D=B4=20=EC=95=84?= =?UTF-8?q?=EB=8B=8C=20=EA=B2=BD=EC=9A=B0,=20=EC=9E=98=EB=AA=BB=EB=90=9C?= =?UTF-8?q?=20=ED=86=A0=ED=81=B0=20=EC=97=90=EB=9F=AC=EA=B0=80=20=EC=95=84?= =?UTF-8?q?=EB=8B=8C=20=EC=9E=98=EB=AA=BB=EB=90=9C=20=EC=84=9C=EB=AA=85=20?= =?UTF-8?q?=EC=95=8C=EA=B3=A0=EB=A6=AC=EC=A6=98=20=EC=97=90=EB=9F=AC?= =?UTF-8?q?=EB=A5=BC=20=EB=B0=9C=EC=83=9D=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=ED=96=88=EC=96=B4=EC=9A=94.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InvalidSignatureAlgorithmException.java | 10 ++++++++ .../exception/error/AuthErrorProperty.java | 1 + .../domain/auth/service/TokenService.java | 23 +++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 src/main/java/com/bamdoliro/maru/domain/auth/exception/InvalidSignatureAlgorithmException.java diff --git a/src/main/java/com/bamdoliro/maru/domain/auth/exception/InvalidSignatureAlgorithmException.java b/src/main/java/com/bamdoliro/maru/domain/auth/exception/InvalidSignatureAlgorithmException.java new file mode 100644 index 00000000..00f23b16 --- /dev/null +++ b/src/main/java/com/bamdoliro/maru/domain/auth/exception/InvalidSignatureAlgorithmException.java @@ -0,0 +1,10 @@ +package com.bamdoliro.maru.domain.auth.exception; + +import com.bamdoliro.maru.domain.auth.exception.error.AuthErrorProperty; +import com.bamdoliro.maru.shared.error.MaruException; + +public class InvalidSignatureAlgorithmException extends MaruException { + public InvalidSignatureAlgorithmException() { + super(AuthErrorProperty.INVALID_SIGNATURE_ALGORITHM); + } +} diff --git a/src/main/java/com/bamdoliro/maru/domain/auth/exception/error/AuthErrorProperty.java b/src/main/java/com/bamdoliro/maru/domain/auth/exception/error/AuthErrorProperty.java index b613c966..faa7930a 100644 --- a/src/main/java/com/bamdoliro/maru/domain/auth/exception/error/AuthErrorProperty.java +++ b/src/main/java/com/bamdoliro/maru/domain/auth/exception/error/AuthErrorProperty.java @@ -12,6 +12,7 @@ public enum AuthErrorProperty implements ErrorProperty { EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, "만료된 토큰입니다."), INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다."), EMPTY_TOKEN(HttpStatus.UNAUTHORIZED, "인증이 필요합니다."), + INVALID_SIGNATURE_ALGORITHM(HttpStatus.UNAUTHORIZED, "유효하지 않은 서명 알고리즘입니다."), AUTHORITY_MISMATCH(HttpStatus.UNAUTHORIZED, "권한이 없습니다."), WRONG_LOGIN(HttpStatus.UNAUTHORIZED, "사용자가 없거나 비밀번호가 틀렸습니다."), ; diff --git a/src/main/java/com/bamdoliro/maru/domain/auth/service/TokenService.java b/src/main/java/com/bamdoliro/maru/domain/auth/service/TokenService.java index a2df3388..4a781564 100644 --- a/src/main/java/com/bamdoliro/maru/domain/auth/service/TokenService.java +++ b/src/main/java/com/bamdoliro/maru/domain/auth/service/TokenService.java @@ -3,11 +3,15 @@ import com.bamdoliro.maru.domain.auth.domain.Token; import com.bamdoliro.maru.domain.auth.domain.type.TokenType; import com.bamdoliro.maru.domain.auth.exception.ExpiredTokenException; +import com.bamdoliro.maru.domain.auth.exception.InvalidSignatureAlgorithmException; import com.bamdoliro.maru.domain.auth.exception.InvalidTokenException; import com.bamdoliro.maru.domain.user.domain.User; import com.bamdoliro.maru.domain.user.service.UserFacade; import com.bamdoliro.maru.infrastructure.persistence.auth.TokenRepository; import com.bamdoliro.maru.shared.config.properties.JwtProperties; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; @@ -18,7 +22,9 @@ import java.nio.charset.StandardCharsets; import java.security.Key; +import java.util.Base64; import java.util.Date; +import java.util.Map; @RequiredArgsConstructor @Service @@ -71,6 +77,8 @@ public String getType(String token) { private Claims extractAllClaims(String token) { try { + validateSignatureAlgorithm(token); + return Jwts.parserBuilder() .setSigningKey(getSigningKey(jwtProperties.getSecretKey())) .build() @@ -78,11 +86,26 @@ private Claims extractAllClaims(String token) { .getBody(); } catch (ExpiredJwtException e) { throw new ExpiredTokenException(); + } catch (InvalidSignatureAlgorithmException e) { + throw e; } catch (Exception e) { throw new InvalidTokenException(); } } + private void validateSignatureAlgorithm(String token) throws JsonProcessingException { + String[] tokenParts = token.split("\\."); + String headerJson = new String(Base64.getDecoder().decode(tokenParts[0])); + + ObjectMapper objectMapper = new ObjectMapper(); + Map headerMap = objectMapper.readValue(headerJson, new TypeReference<>() {}); + String algorithm = (String) headerMap.get("alg"); + + if (!algorithm.equals(SignatureAlgorithm.HS256.getValue())) { + throw new InvalidSignatureAlgorithmException(); + } + } + private Key getSigningKey(String secretKey) { byte[] keyBytes = secretKey.getBytes(StandardCharsets.UTF_8); return Keys.hmacShaKeyFor(keyBytes); From 5a9f4090cc0f349547271583c6d61b5bcbd77b82 Mon Sep 17 00:00:00 2001 From: cabbage16 Date: Thu, 10 Oct 2024 14:39:36 +0900 Subject: [PATCH 2/5] =?UTF-8?q?test(#145):=20JWT=20=EC=84=9C=EB=AA=85=20?= =?UTF-8?q?=EC=95=8C=EA=B3=A0=EB=A6=AC=EC=A6=98=20=EB=B6=88=EC=9D=BC?= =?UTF-8?q?=EC=B9=98=20=EC=8B=9C=20=EC=97=90=EB=9F=AC=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20-=20JWT=20=EC=84=9C=EB=AA=85?= =?UTF-8?q?=20=EC=95=8C=EA=B3=A0=EB=A6=AC=EC=A6=98=EC=9D=B4=20=EC=9E=98?= =?UTF-8?q?=EB=AA=BB=EB=90=98=EC=97=88=EC=9D=84=20=EB=95=8C,=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EB=A5=BC=20=EB=B0=9C=EC=83=9D=EC=8B=9C=ED=82=A4?= =?UTF-8?q?=EB=8A=94=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=A5=BC=20=EB=A7=8C?= =?UTF-8?q?=EB=93=A4=EC=97=88=EC=96=B4=EC=9A=94.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/auth/AuthControllerTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/test/java/com/bamdoliro/maru/presentation/auth/AuthControllerTest.java b/src/test/java/com/bamdoliro/maru/presentation/auth/AuthControllerTest.java index b9b5b684..fb1e7b80 100644 --- a/src/test/java/com/bamdoliro/maru/presentation/auth/AuthControllerTest.java +++ b/src/test/java/com/bamdoliro/maru/presentation/auth/AuthControllerTest.java @@ -1,6 +1,7 @@ package com.bamdoliro.maru.presentation.auth; import com.bamdoliro.maru.domain.auth.exception.ExpiredTokenException; +import com.bamdoliro.maru.domain.auth.exception.InvalidSignatureAlgorithmException; import com.bamdoliro.maru.domain.auth.exception.InvalidTokenException; import com.bamdoliro.maru.domain.user.domain.User; import com.bamdoliro.maru.domain.user.exception.PasswordMismatchException; @@ -183,6 +184,22 @@ class AuthControllerTest extends RestDocsTestSupport { verify(refreshTokenUseCase, never()).execute(anyString()); } + @Test + void 토큰의_서명_알고리즘이_불일치하면_에러가_발생한다() throws Exception { + String accessToken = AuthFixture.createAccessTokenString(); + doThrow(new InvalidSignatureAlgorithmException()).when(refreshTokenUseCase).execute(accessToken); + + mockMvc.perform(patch("/auth") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header("Refresh-Token", accessToken) + ) + + .andExpect(status().isUnauthorized()) + + .andDo(restDocs.document()); + } + @Test void 유저가_로그아웃한다() throws Exception { User user = UserFixture.createUser(); From 1848fcbe37d2b0688c604c3411010ad8b93d0bef Mon Sep 17 00:00:00 2001 From: cabbage16 Date: Thu, 10 Oct 2024 15:39:18 +0900 Subject: [PATCH 3/5] =?UTF-8?q?docs(#145):=20JWT=20=EC=84=9C=EB=AA=85=20?= =?UTF-8?q?=EC=95=8C=EA=B3=A0=EB=A6=AC=EC=A6=98=20=EB=B6=88=EC=9D=BC?= =?UTF-8?q?=EC=B9=98=20=EC=8B=9C=20=EC=98=A4=EB=A5=98=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=20=EB=AC=B8=EC=84=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/auth.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/docs/asciidoc/auth.adoc b/src/docs/asciidoc/auth.adoc index f61781e3..e98a70aa 100644 --- a/src/docs/asciidoc/auth.adoc +++ b/src/docs/asciidoc/auth.adoc @@ -50,6 +50,9 @@ include::{snippets}/auth-controller-test/액세스_토큰으로_액세스_토큰 ===== 리프레시 토큰을 보내지 않은 경우 include::{snippets}/auth-controller-test/액세스_토큰을_재발급할_때_리프레시_토큰을_보내지_않으면_에러가_발생한다/http-response.adoc[] +===== 서명 알고리즘이 유효하지 않은 경우 +include::{snippets}/auth-controller-test/토큰의_서명_알고리즘이_불일치하면_에러가_발생한다/http-response.adoc[] + === 로그아웃 로그인 한 사용자는 로그아웃 할 수 있습니다. From 36a976e523e514ce5cf15ac259d6ab578829b1c6 Mon Sep 17 00:00:00 2001 From: cabbage16 Date: Thu, 10 Oct 2024 15:42:26 +0900 Subject: [PATCH 4/5] =?UTF-8?q?docs(#145):=20JWT=20=EC=84=9C=EB=AA=85=20?= =?UTF-8?q?=EC=95=8C=EA=B3=A0=EB=A6=AC=EC=A6=98=20=EB=B6=88=EC=9D=BC?= =?UTF-8?q?=EC=B9=98=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/auth.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/asciidoc/auth.adoc b/src/docs/asciidoc/auth.adoc index e98a70aa..98530e11 100644 --- a/src/docs/asciidoc/auth.adoc +++ b/src/docs/asciidoc/auth.adoc @@ -50,7 +50,7 @@ include::{snippets}/auth-controller-test/액세스_토큰으로_액세스_토큰 ===== 리프레시 토큰을 보내지 않은 경우 include::{snippets}/auth-controller-test/액세스_토큰을_재발급할_때_리프레시_토큰을_보내지_않으면_에러가_발생한다/http-response.adoc[] -===== 서명 알고리즘이 유효하지 않은 경우 +===== 서명 알고리즘이 불일치 하는 경우 include::{snippets}/auth-controller-test/토큰의_서명_알고리즘이_불일치하면_에러가_발생한다/http-response.adoc[] === 로그아웃 From 3bd2e51a6ba46bd780689cf1772958dd5679e411 Mon Sep 17 00:00:00 2001 From: cabbage16 Date: Fri, 11 Oct 2024 00:02:56 +0900 Subject: [PATCH 5/5] =?UTF-8?q?perf(#145):=20InvalidSignatureAlgorithmExce?= =?UTF-8?q?ption=20=EC=82=AD=EC=A0=9C=20-=20=EC=84=9C=EB=AA=85=20=EC=95=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=EC=A6=98=EC=9D=B4=20=EC=9C=A0=ED=9A=A8?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8B=A4=EB=8A=94=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EB=8C=80=EC=8B=A0=20=ED=86=A0=ED=81=B0=EC=9D=B4=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=ED=95=98=EC=A7=80=20=EC=95=8A=EC=9D=80=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=EB=A1=9C=20=EC=B2=98=EB=A6=AC=ED=96=88?= =?UTF-8?q?=EC=96=B4=EC=9A=94.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/InvalidSignatureAlgorithmException.java | 10 ---------- .../domain/auth/exception/error/AuthErrorProperty.java | 1 - .../maru/domain/auth/service/TokenService.java | 5 +---- .../maru/presentation/auth/AuthControllerTest.java | 3 +-- 4 files changed, 2 insertions(+), 17 deletions(-) delete mode 100644 src/main/java/com/bamdoliro/maru/domain/auth/exception/InvalidSignatureAlgorithmException.java diff --git a/src/main/java/com/bamdoliro/maru/domain/auth/exception/InvalidSignatureAlgorithmException.java b/src/main/java/com/bamdoliro/maru/domain/auth/exception/InvalidSignatureAlgorithmException.java deleted file mode 100644 index 00f23b16..00000000 --- a/src/main/java/com/bamdoliro/maru/domain/auth/exception/InvalidSignatureAlgorithmException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.bamdoliro.maru.domain.auth.exception; - -import com.bamdoliro.maru.domain.auth.exception.error.AuthErrorProperty; -import com.bamdoliro.maru.shared.error.MaruException; - -public class InvalidSignatureAlgorithmException extends MaruException { - public InvalidSignatureAlgorithmException() { - super(AuthErrorProperty.INVALID_SIGNATURE_ALGORITHM); - } -} diff --git a/src/main/java/com/bamdoliro/maru/domain/auth/exception/error/AuthErrorProperty.java b/src/main/java/com/bamdoliro/maru/domain/auth/exception/error/AuthErrorProperty.java index faa7930a..b613c966 100644 --- a/src/main/java/com/bamdoliro/maru/domain/auth/exception/error/AuthErrorProperty.java +++ b/src/main/java/com/bamdoliro/maru/domain/auth/exception/error/AuthErrorProperty.java @@ -12,7 +12,6 @@ public enum AuthErrorProperty implements ErrorProperty { EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, "만료된 토큰입니다."), INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다."), EMPTY_TOKEN(HttpStatus.UNAUTHORIZED, "인증이 필요합니다."), - INVALID_SIGNATURE_ALGORITHM(HttpStatus.UNAUTHORIZED, "유효하지 않은 서명 알고리즘입니다."), AUTHORITY_MISMATCH(HttpStatus.UNAUTHORIZED, "권한이 없습니다."), WRONG_LOGIN(HttpStatus.UNAUTHORIZED, "사용자가 없거나 비밀번호가 틀렸습니다."), ; diff --git a/src/main/java/com/bamdoliro/maru/domain/auth/service/TokenService.java b/src/main/java/com/bamdoliro/maru/domain/auth/service/TokenService.java index 4a781564..4b0777ce 100644 --- a/src/main/java/com/bamdoliro/maru/domain/auth/service/TokenService.java +++ b/src/main/java/com/bamdoliro/maru/domain/auth/service/TokenService.java @@ -3,7 +3,6 @@ import com.bamdoliro.maru.domain.auth.domain.Token; import com.bamdoliro.maru.domain.auth.domain.type.TokenType; import com.bamdoliro.maru.domain.auth.exception.ExpiredTokenException; -import com.bamdoliro.maru.domain.auth.exception.InvalidSignatureAlgorithmException; import com.bamdoliro.maru.domain.auth.exception.InvalidTokenException; import com.bamdoliro.maru.domain.user.domain.User; import com.bamdoliro.maru.domain.user.service.UserFacade; @@ -86,8 +85,6 @@ private Claims extractAllClaims(String token) { .getBody(); } catch (ExpiredJwtException e) { throw new ExpiredTokenException(); - } catch (InvalidSignatureAlgorithmException e) { - throw e; } catch (Exception e) { throw new InvalidTokenException(); } @@ -102,7 +99,7 @@ private void validateSignatureAlgorithm(String token) throws JsonProcessingExcep String algorithm = (String) headerMap.get("alg"); if (!algorithm.equals(SignatureAlgorithm.HS256.getValue())) { - throw new InvalidSignatureAlgorithmException(); + throw new InvalidTokenException(); } } diff --git a/src/test/java/com/bamdoliro/maru/presentation/auth/AuthControllerTest.java b/src/test/java/com/bamdoliro/maru/presentation/auth/AuthControllerTest.java index fb1e7b80..e9a23b03 100644 --- a/src/test/java/com/bamdoliro/maru/presentation/auth/AuthControllerTest.java +++ b/src/test/java/com/bamdoliro/maru/presentation/auth/AuthControllerTest.java @@ -1,7 +1,6 @@ package com.bamdoliro.maru.presentation.auth; import com.bamdoliro.maru.domain.auth.exception.ExpiredTokenException; -import com.bamdoliro.maru.domain.auth.exception.InvalidSignatureAlgorithmException; import com.bamdoliro.maru.domain.auth.exception.InvalidTokenException; import com.bamdoliro.maru.domain.user.domain.User; import com.bamdoliro.maru.domain.user.exception.PasswordMismatchException; @@ -187,7 +186,7 @@ class AuthControllerTest extends RestDocsTestSupport { @Test void 토큰의_서명_알고리즘이_불일치하면_에러가_발생한다() throws Exception { String accessToken = AuthFixture.createAccessTokenString(); - doThrow(new InvalidSignatureAlgorithmException()).when(refreshTokenUseCase).execute(accessToken); + doThrow(new InvalidTokenException()).when(refreshTokenUseCase).execute(accessToken); mockMvc.perform(patch("/auth") .accept(MediaType.APPLICATION_JSON)