버전 정보
java: openjdk 17.0.8
spring: Spring Boot 3.2.1
nimbus-jose-jwt: 9.31
프로젝트 생성
start.spring.io 에서 위와 같이 프로젝트 생성
JWK & JWKS
JWK (JSON Web Key)
Json Web Key (JWK)는 JSON 형식으로 표현된 공개키 또는 비밀키를 나타내는 표준
JWK는 웹에서 사용되는 서비스 및 애플리케이션 간에 공유되는 키의 표준 표현 방법을 제공
JWK의 기본적인 구성
- kty (Key Type): 키의 유형을 나타냄. RSA, EC, OCT 등이 있음
- use (Key Use): 키의 사용 용도를 나타냄. 서명용인지, 암호화용인지 등을 지정
- kid (Key ID): 키의 고유 식별자
- alg (Algorithm): 키의 알고리즘을 나타냄. RSA 키의 경우 RS256, RS384, RS512 등이 있음
- 특정 키 유형에 따라 추가 필드 존재 (RSA의 경우 'n', 'e' 필드 등)
JWKS(JSON Web Key Set)
JSON Web Key Set (JWKS)는 여러 JWK를 모은 JSON 문서. 일반적으로 공개키를 JWKS로 표현하여 클라이언트가 동적으로 공개키를 가져와 JWT의 검증 등에 활용할 수 있게 함
JWKS는 배열 형태로 여러 키를 포함하고 있음
JWKS는 기본적으로 다음과 같이 JWK들의 배열인 keys 필드로 구성
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"kid": "key-id-1",
"alg": "RS256",
"n": "base64url",
"e": "base64url"
},
{
"kty": "RSA",
"use": "sig",
"kid": "key-id-2",
"alg": "RS256",
"n": "base64url",
"e": "base64url"
},
// 다른 키들...
]
}
JWKS를 사용하면 클라이언트는 동적으로 서버에서 공개키를 가져와 JWT의 검증 등에 사용할 수 있음
JWKS는 웹 표준으로 정의되어 있으며, 여러 인증 및 보안 관련 프로토콜에서 사용됨
API 구현
JWKS 관련 라이브러리 추가
dependencies {
...
implementation group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: '9.31'
}
JWKS 관련 로직을 처리하는 JwtManager 구현
@Slf4j
@Component
public class JwtManager {
private final KeyPair keyPair;
private final JWKSet jwks;
// constructor
public JwtManager() {
// 공개키, 비밀키 생성 (for test)
// 키가 따로 있다면 가져와 사용
this.keyPair = generateKeyPair();
this.jwks = new JWKSet(
new RSAKey.Builder((RSAPublicKey) keyPair.getPublic())
.privateKey(keyPair.getPrivate())
.keyUse(KeyUse.SIGNATURE)
.algorithm(JWSAlgorithm.RS256)
.keyID(UUID.randomUUID().toString())
.issueTime(new Date())
.build()
);
loggingKey(keyPair);
}
private KeyPair generateKeyPair() {
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
return keyPairGenerator.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
// 프로그램 실행 되면 constructor에서 생성한 pub, prv key를 출력
private void loggingKey(KeyPair keyPair) {
byte[] pubBytes = keyPair.getPublic().getEncoded();
byte[] prvBytes = keyPair.getPrivate().getEncoded();
Base64.Encoder encoder = Base64.getEncoder();
log.info("Public: {}", encoder.encodeToString(pubBytes));
log.info("Private: {}", encoder.encodeToString(prvBytes));
}
// JWKS를 반환하는 메서드
public Map<String, Object> getJwks() {
return jwks.toPublicJWKSet().toJSONObject();
}
}
Request를 받을 Controller, Service 구현
@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor // lombok
public class AuthController {
private final AuthService authService;
@GetMapping("/jwks")
public ResponseEntity<Map<String, Object>> jwks() {
Map<String, Object> jwkSet = authService.getJwks();
return ResponseEntity.ok()
.body(jwkSet);
}
}
---
@Service
@RequiredArgsConstructor
public class AuthService {
// 위에서 구현한 JwtManager 주입
private final JwtManager jwtManager;
public Map<String, Object> getJwks() {
return jwtManager.getJwks();
}
}
이제 http://localhost:8080/api/auth/jwks 로 요청을 보내보면 다음과 같은 응답을 받을 수 있음
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "d2f5ce98-df68-491f-b570-3e0e56c6ebf2",
"alg": "RS256",
"iat": 1704951898,
"n": "qY6U1JMNZkgHQnncdYf9aUCZQ2gJbTgu9lQMffZwFDaFHin3LZJRB7U3pS9HXXh1mKsFb_HXTfsO76kgPJqcPftP6wFm209XpCJTsPt7-aaCSRblVFpLjZQYMbuK_dXV-DK6RR1Ks1Z0w76qjtcKSoRjr4wkcYAr3FF_xJZjKYXomM9USfybpGjKaPQIpwkiYVTwX40ZUiFiEkv6pdiDLCq_97qwZ-5EiseNuj3LyM27nR0aBXJyyLOZWrbi9PXYpHKbpvPRi1rCI7dZndads7pf2nFEjFLlPvfBm0kKjt8PHclEyCn376VAmIoZ44onHXxOx9DDphmNo9TVfj48zQ"
}
]
}
클라이언트에게 위에서 구현한 API를 제공하여 JWKS를 통해 JWT를 검증할 수 있도록 함
'Spring' 카테고리의 다른 글
Spring Cloud Gateway 구현 정리 (1) | 2024.02.06 |
---|---|
Java Spring JWT 생성 및 검증 로직 구현 (0) | 2024.01.11 |
Spring JPA @Id 사용시 GenerationType (2) | 2023.12.05 |
Spring JPA hibernate.ddl-auto 정리 (0) | 2023.12.04 |
Spring @RestControllerAdvice 적용 (0) | 2023.11.09 |
댓글