1 λΆ„ μ†Œμš”

1. JWT API μ„€μ • 및 검증 ν”„λ‘œμ„ΈμŠ€ 이해

βœ“ μ„€μ • 클래슀 생성

@Configuration(proxyBeanMethods = false)
public class OAuth2ResourceServerConfig {

	@Bean
	SecurityFilterChain jwtSecurityFilterChain(HttpSecurity http) throws Exception {
		http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
		http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt); // jwt 토큰을 κ²€μ¦ν•˜λŠ” λΉˆλ“€κ³Ό 클래슀λ₯Ό μƒμ„±ν•˜κ³  μ΄ˆκΈ°ν™” 함
		return http.build();
	}

}
  • API μ„€μ •
    • SecurityFilterChain νƒ€μž…μ˜ λΉˆμ„ μƒμ„±ν•΄μ„œ λ³΄μ•ˆ ν•„ν„°λ₯Ό κ΅¬μ„±ν•œλ‹€
    • HttpSecurity 에 μžˆλŠ” oauth2ResourceServer().jwt() API λ₯Ό μ •μ˜ν•˜κ³  λΉŒλ“œν•œλ‹€

βœ“ 검증 ν”„λ‘œμ„ΈμŠ€ 이해

spring.security.oauth2.resourceserver.jwt.issuer-uri = http://localhost:8080/realms/oauth2
  • ν”„λ‘œνΌν‹°λ₯Ό μ„€μ •ν•˜λ©΄ JWT둜 μΈμ½”λ”©ν•œ Bearer 토큰을 κ²€μ¦ν•˜λŠ” λ¦¬μ†ŒμŠ€ μ„œλ²„κ°€ μžλ™μœΌλ‘œ μ„€μ •λœλ‹€.
  • Open ID Connect Provider μ„€μ • μ—”λ“œν¬μΈνŠΈ λ˜λŠ” 인가 μ„œλ²„ 메타데이터 μ—”λ“œν¬μΈνŠΈλ₯Ό κ²€μƒ‰ν•΄μ„œ jwk-set-url μ—”λ“œν¬μΈνŠΈλ₯Ό μ°Ύμ•„ 검증을 μ§„ν–‰ν•œλ‹€
  • 두가지 검증 μ „λž΅μ„ μ„€μ •ν•œλ‹€
    • λ¦¬μ†ŒμŠ€ μ„œλ²„λŠ” μΈκ°€μ„œλ²„μ˜ jwk-set-uri μ—”λ“œν¬μΈνŠΈλ‘œ μœ νš¨ν•œ κ³΅κ°œν‚€λ₯Ό μ§ˆμ˜ν•˜κΈ° μœ„ν•œ 검증 μ „λž΅μ„ μ„€μ •ν•œλ‹€
    • Issuer-uri(http://localhost:8080/realms/oauth2) 에 λŒ€ν•œ 각 JWT ν΄λ ˆμž„μ„ 검증할 μ „λž΅μ„ μ„€μ •ν•œλ‹€

βœ“ 검증 μˆœμ„œ

  1. ν΄λΌμ΄μ–ΈνŠΈκ°€ Authorization Bearer token-value λ₯Ό 헀더에 λ‹΄μ•„μ„œ μš”μ²­ν•œλ‹€
  2. λ¦¬μ†ŒμŠ€ μ„œλ²„λŠ” μš”μ²­ν•œ 토큰이 Bearer 토큰 사양에 λΆ€ν•©ν•˜λŠ”μ§€ κ²€μ‚¬ν•œλ‹€
  3. μΈκ°€μ„œλ²„μ—μ„œ JWT 토큰에 μ„œλͺ…ν•œ κ°œμΈν‚€μ™€ λ§€μΉ­ν•˜λŠ” κ³΅κ°œν‚€λ₯Ό jwk-set-url μ—”λ“œν¬μΈνŠΈ μš”μ²­μœΌλ‘œ κ°€μ Έμ™€μ„œ 첫번째 검증을 μ§„ν–‰ν•œλ‹€.
  4. JWT에 μžˆλŠ” exp, nbf, iss ν΄λ ˆμž„μ˜ 정보가 기쀀에 λΆ€ν•©ν•˜λŠ”μ§€ λ‘λ²ˆμ§Έ 검증을 μ§„ν–‰ν•œλ‹€
  5. 검증에 μ„±κ³΅ν•˜λ©΄ Jwt 객체λ₯Ό μƒμ„±ν•˜κ³  claims 정보에 μžˆλŠ” scope λ₯Ό μΆ”μΆœν•΄μ„œ μ‹œνλ¦¬ν‹°μ˜ κΆŒν•œμ— λ§€ν•‘ν•œλ‹€ (SCOPE_profile, SCOPE_email)
  6. Authentication 객체λ₯Ό μƒμ„±ν•˜κ³  Jwt 객체λ₯Ό principal μ†μ„±μ—λŠ” μ €μž₯ν•œλ‹€
  7. Authentication λ₯Ό SecurityContext 에 μ €μž₯ν•˜κ³  인증을 μ™„λ£Œν•œλ‹€

2. JwtDecoder 이해

2-1). μ†Œκ°œ 및 μ„ΈλΆ€ 흐름

βœ“ JwtDecoder μ†Œκ°œ

  • JwtDecoder λŠ” λ¬Έμžμ—΄λ‘œ 된 JWT(JSON Web Token)λ₯Ό 컴팩트 ν΄λ ˆμž„ ν‘œν˜„ ν˜•μ‹μ—μ„œ Jwt μΈμŠ€ν„΄μŠ€λ‘œ β€œλ””μ½”λ”©β€ν•˜λŠ” 역할을 ν•œλ‹€.
  • JwtDecoder λŠ” JWT κ°€ JSON μ›Ή μ„œλͺ…(JWS) ꡬ쑰둜 μƒμ„±λœ 경우 JWS μ„œλͺ…에 λŒ€ν•œ κ²€μ¦μ˜ μ±…μž„μ΄ μžˆλ‹€.
  • κΈ°λ³Έ κ΅¬ν˜„μ²΄λ‘œ NimbusJwtDecoder κ°€ μžˆλ‹€

βœ“ NimbusJwtDecoder

βœ“ 검증 μ„ΈλΆ€ 흐름

  • JwtDecoder 의 decode() λ₯Ό 톡해 검증에 μ„±κ³΅ν•˜λ©΄ μ΅œμ’…μ μœΌλ‘œ Jwt νƒ€μž…μ˜ 인증객체λ₯Ό λ°˜ν™˜ν•œλ‹€

2-2). 생성 방법

βœ“ JwtDecoders.fromIssuerLocation()

@Bean
public JwtDecoder jwtDecoder() {
	return JwtDecoders.fromIssuerLocation(properties.getIssuerUri());
}
  • JwtDecoders.fromIssuerLocation() 을 ν˜ΈμΆœν•˜λ©΄ Provider μ„€μ • λ˜λŠ” 인가 μ„œλ²„ 메타데이터 μ—”λ“œν¬μΈνŠΈλ‘œ JWK μ…‹ Uriλ₯Ό μš”μ²­ν•œλ‹€
  • μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ λ”°λ‘œ μ •μ˜ν•œ JwtDecoder 빈이 μ—†λ‹€λ©΄ μŠ€ν”„λ§ λΆ€νŠΈκ°€ μœ„μ— μžˆλŠ” λ””ν΄νŠΈ λΉˆμ„ λ“±λ‘ν•œλ‹€.

βœ“ NimbusJwtDecoder.withJwkSetUri()

@Bean
public JwtDecoder jwtDecoder() {
	return NimbusJwtDecoder.withJwkSetUri(properties.getJwkSetUri())
		.jwsAlgorithm(SignatureAlgorithm.RS512).build();
}
  • 기본적으둜 μŠ€ν”„λ§ λΆ€νŠΈμ— μ˜ν•΄ NimbusJwtDecoder 빈이 μžλ™ 생성될 경우 λ¦¬μ†ŒμŠ€ μ„œλ²„λŠ” RS256 을 μ‚¬μš©ν•œ ν† ν°λ§Œ μ‹ λ’°ν•˜κ³  이 ν† ν°λ§Œ 검증할 수 μžˆλ‹€
  • JwkSetUri 에 μ˜ν•œ κ²€μ¦λ°©μ‹μœΌλ‘œ NimbusJwtDecoder λ₯Ό 생성할 경우 μ•Œκ³ λ¦¬μ¦˜μ˜ μ’…λ₯˜λ₯Ό λ³€κ²½ν•  수 μžˆμœΌλ‚˜ RSA μ•Œκ³ λ¦¬μ¦˜μ— ν•œν•΄ 변경이 κ°€λŠ₯ν•˜κ³  HMAC 은 μ§€μ›ν•˜μ§€ μ•ŠλŠ”λ‹€

μΉ΄ν…Œκ³ λ¦¬:

μ—…λ°μ΄νŠΈ:

λŒ“κΈ€λ‚¨κΈ°κΈ°