From e8b199f163697b000f055cb4f6d2031fd63b3507 Mon Sep 17 00:00:00 2001 From: MinJun Choi Date: Fri, 20 Jun 2025 20:06:03 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20JJWT=20=EB=9D=BC=EC=9D=B4=EB=B8=8C?= =?UTF-8?q?=EB=9F=AC=EB=A6=AC=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=8F=20JWT=20=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - jjwt 라이브러리 버전 0.9.1 → 0.11.5로 업데이트 - `JwtTokenProvider` 내 키 생성 방식 개선 (Base64 디코딩 후 `Keys.hmacShaKeyFor` 사용) - `io.jsonwebtoken` 최신 방식 API로 전환 (`JwtParserBuilder` 활용) - `application.properties`에서 secret key 값 업데이트 - 종속성 추가 (`jjwt-api`, `jjwt-impl`, `jjwt-jackson`) --- spring-auth-1/complete/build.gradle | 5 +- .../auth/infrastructure/JwtTokenProvider.java | 48 ++++++++++++++----- .../cholog/auth/ui/TokenLoginController.java | 2 +- .../src/main/resources/application.properties | 4 +- spring-auth-1/initial/build.gradle | 5 +- .../auth/infrastructure/JwtTokenProvider.java | 48 ++++++++++++++----- .../cholog/auth/ui/TokenLoginController.java | 2 +- .../src/main/resources/application.properties | 4 +- 8 files changed, 82 insertions(+), 36 deletions(-) diff --git a/spring-auth-1/complete/build.gradle b/spring-auth-1/complete/build.gradle index 612af60e..a5370b8f 100644 --- a/spring-auth-1/complete/build.gradle +++ b/spring-auth-1/complete/build.gradle @@ -15,8 +15,9 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'io.jsonwebtoken:jjwt:0.9.1' - implementation 'javax.xml.bind:jaxb-api:2.3.1' + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' + implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.rest-assured:rest-assured:5.3.1' diff --git a/spring-auth-1/complete/src/main/java/cholog/auth/infrastructure/JwtTokenProvider.java b/spring-auth-1/complete/src/main/java/cholog/auth/infrastructure/JwtTokenProvider.java index d1a75c38..e415789f 100644 --- a/spring-auth-1/complete/src/main/java/cholog/auth/infrastructure/JwtTokenProvider.java +++ b/spring-auth-1/complete/src/main/java/cholog/auth/infrastructure/JwtTokenProvider.java @@ -1,17 +1,30 @@ package cholog.auth.infrastructure; -import io.jsonwebtoken.*; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import java.security.Key; +import java.util.Base64; +import java.util.Date; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import java.util.Date; - @Component public class JwtTokenProvider { - @Value("${security.jwt.token.secret-key}") - private String secretKey; - @Value("${security.jwt.token.expire-length}") - private long validityInMilliseconds; + private final Key secretKey; + private final long validityInMilliseconds; + + public JwtTokenProvider( + @Value("${security.jwt.token.secret-key}") String rawSecretKey, + @Value("${security.jwt.token.expire-length}") Long validityInMilliseconds + ) { + final byte[] keyBytes = Base64.getDecoder().decode(rawSecretKey); + this.secretKey = Keys.hmacShaKeyFor(keyBytes); + this.validityInMilliseconds = validityInMilliseconds; + } + public String createToken(String payload) { Claims claims = Jwts.claims().setSubject(payload); @@ -22,21 +35,30 @@ public String createToken(String payload) { .setClaims(claims) .setIssuedAt(now) .setExpiration(validity) - .signWith(SignatureAlgorithm.HS256, secretKey) + .signWith(secretKey) .compact(); } public String getPayload(String token) { - return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject(); + validateToken(token); + return Jwts.parserBuilder() + .setSigningKey(secretKey) + .build() + .parseClaimsJws(token) + .getBody() + .getSubject(); } - public boolean validateToken(String token) { + private void validateToken(String token) { try { - Jws claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token); + Jws claims = Jwts.parserBuilder() + .setSigningKey(secretKey) + .build() + .parseClaimsJws(token); - return !claims.getBody().getExpiration().before(new Date()); + claims.getBody().getExpiration(); } catch (JwtException | IllegalArgumentException e) { - return false; + throw new RuntimeException("Invalid token"); } } } diff --git a/spring-auth-1/complete/src/main/java/cholog/auth/ui/TokenLoginController.java b/spring-auth-1/complete/src/main/java/cholog/auth/ui/TokenLoginController.java index 59def57b..e821ecf2 100644 --- a/spring-auth-1/complete/src/main/java/cholog/auth/ui/TokenLoginController.java +++ b/spring-auth-1/complete/src/main/java/cholog/auth/ui/TokenLoginController.java @@ -45,7 +45,7 @@ public ResponseEntity tokenLogin(@RequestBody TokenRequest tokenR * ex) request sample *

* GET /members/me/token HTTP/1.1 - * authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJlbWFpbEBlbWFpbC5jb20iLCJpYXQiOjE2MTAzNzY2NzIsImV4cCI6MTYxMDM4MDI3Mn0.Gy4g5RwK1Nr7bKT1TOFS4Da6wxWh8l97gmMQDgF8c1E + * authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJlbWFpbEBlbWFpbC5jb20iLCJpYXQiOjE3NTA0MTY1MzksImV4cCI6MTc1MDQyMDEzOX0.mhaPDrphpRE9mE1YEZreOmDjZCapBrFe-K4SZvXMlAY * accept: application/json */ @GetMapping("/members/me/token") diff --git a/spring-auth-1/complete/src/main/resources/application.properties b/spring-auth-1/complete/src/main/resources/application.properties index dad5b3cf..bc60a822 100644 --- a/spring-auth-1/complete/src/main/resources/application.properties +++ b/spring-auth-1/complete/src/main/resources/application.properties @@ -1,2 +1,2 @@ -security.jwt.token.secret-key=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjJ9.ih1aovtQShabQ7l0cINw4k1fagApg3qLWiB8Kt59Lno -security.jwt.token.expire-length=3600000 \ No newline at end of file +security.jwt.token.secret-key=/BWxvVt/eMsTVSq+RI9kRCrZKK38KNGIWi7ilxCg9So= +security.jwt.token.expire-length=3600000 diff --git a/spring-auth-1/initial/build.gradle b/spring-auth-1/initial/build.gradle index 612af60e..a5370b8f 100644 --- a/spring-auth-1/initial/build.gradle +++ b/spring-auth-1/initial/build.gradle @@ -15,8 +15,9 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'io.jsonwebtoken:jjwt:0.9.1' - implementation 'javax.xml.bind:jaxb-api:2.3.1' + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' + implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.rest-assured:rest-assured:5.3.1' diff --git a/spring-auth-1/initial/src/main/java/cholog/auth/infrastructure/JwtTokenProvider.java b/spring-auth-1/initial/src/main/java/cholog/auth/infrastructure/JwtTokenProvider.java index d1a75c38..e415789f 100644 --- a/spring-auth-1/initial/src/main/java/cholog/auth/infrastructure/JwtTokenProvider.java +++ b/spring-auth-1/initial/src/main/java/cholog/auth/infrastructure/JwtTokenProvider.java @@ -1,17 +1,30 @@ package cholog.auth.infrastructure; -import io.jsonwebtoken.*; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import java.security.Key; +import java.util.Base64; +import java.util.Date; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import java.util.Date; - @Component public class JwtTokenProvider { - @Value("${security.jwt.token.secret-key}") - private String secretKey; - @Value("${security.jwt.token.expire-length}") - private long validityInMilliseconds; + private final Key secretKey; + private final long validityInMilliseconds; + + public JwtTokenProvider( + @Value("${security.jwt.token.secret-key}") String rawSecretKey, + @Value("${security.jwt.token.expire-length}") Long validityInMilliseconds + ) { + final byte[] keyBytes = Base64.getDecoder().decode(rawSecretKey); + this.secretKey = Keys.hmacShaKeyFor(keyBytes); + this.validityInMilliseconds = validityInMilliseconds; + } + public String createToken(String payload) { Claims claims = Jwts.claims().setSubject(payload); @@ -22,21 +35,30 @@ public String createToken(String payload) { .setClaims(claims) .setIssuedAt(now) .setExpiration(validity) - .signWith(SignatureAlgorithm.HS256, secretKey) + .signWith(secretKey) .compact(); } public String getPayload(String token) { - return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject(); + validateToken(token); + return Jwts.parserBuilder() + .setSigningKey(secretKey) + .build() + .parseClaimsJws(token) + .getBody() + .getSubject(); } - public boolean validateToken(String token) { + private void validateToken(String token) { try { - Jws claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token); + Jws claims = Jwts.parserBuilder() + .setSigningKey(secretKey) + .build() + .parseClaimsJws(token); - return !claims.getBody().getExpiration().before(new Date()); + claims.getBody().getExpiration(); } catch (JwtException | IllegalArgumentException e) { - return false; + throw new RuntimeException("Invalid token"); } } } diff --git a/spring-auth-1/initial/src/main/java/cholog/auth/ui/TokenLoginController.java b/spring-auth-1/initial/src/main/java/cholog/auth/ui/TokenLoginController.java index ed0f81ff..32e0a7f1 100644 --- a/spring-auth-1/initial/src/main/java/cholog/auth/ui/TokenLoginController.java +++ b/spring-auth-1/initial/src/main/java/cholog/auth/ui/TokenLoginController.java @@ -46,7 +46,7 @@ public ResponseEntity tokenLogin() { * ex) request sample *

* GET /members/me/token HTTP/1.1 - * authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJlbWFpbEBlbWFpbC5jb20iLCJpYXQiOjE2MTAzNzY2NzIsImV4cCI6MTYxMDM4MDI3Mn0.Gy4g5RwK1Nr7bKT1TOFS4Da6wxWh8l97gmMQDgF8c1E + * authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJlbWFpbEBlbWFpbC5jb20iLCJpYXQiOjE3NTA0MTY1MzksImV4cCI6MTc1MDQyMDEzOX0.mhaPDrphpRE9mE1YEZreOmDjZCapBrFe-K4SZvXMlAY * accept: application/json */ @GetMapping("/members/me/token") diff --git a/spring-auth-1/initial/src/main/resources/application.properties b/spring-auth-1/initial/src/main/resources/application.properties index dad5b3cf..bc60a822 100644 --- a/spring-auth-1/initial/src/main/resources/application.properties +++ b/spring-auth-1/initial/src/main/resources/application.properties @@ -1,2 +1,2 @@ -security.jwt.token.secret-key=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjJ9.ih1aovtQShabQ7l0cINw4k1fagApg3qLWiB8Kt59Lno -security.jwt.token.expire-length=3600000 \ No newline at end of file +security.jwt.token.secret-key=/BWxvVt/eMsTVSq+RI9kRCrZKK38KNGIWi7ilxCg9So= +security.jwt.token.expire-length=3600000