Files
llm-wiki/raw/official-docs/spring-security-resource-server-jwt.md
T

14 KiB

title, source_type, url, archive_url, related_projects, related_branches, tags, status, confidence, created, last_reviewed
title source_type url archive_url related_projects related_branches tags status confidence created last_reviewed
Spring Security — OAuth 2.0 Resource Server JWT (issuer-uri, JwtDecoder) official-doc https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/jwt.html
keycloak-patterns
feature-keycloak-patterns
feature-keycloak-single-ec2-no-google
feature-keycloak-spring-rs-audience-validator
feature-keycloak-spring-rs-role-mapping
feature-keycloak-iss-claim-hostname-mismatch
audience-validator
jwks
jwt-validation
keycloak-patterns
oauth2
oidc
p2a-spa-resource-server
p3a-single-ec2
resource-server
spring-boot
spring-security
official-doc
raw high 2026-05-25 2026-05-27

Spring Security — OAuth 2.0 Resource Server JWT (issuer-uri, JwtDecoder)

Layer: raw/official-docs/ — Spring Security Reference / "OAuth 2.0 Resource Server / JWT" 페이지 verbatim. P2A/P3A 의 Spring Boot Resource Server (/api/me 등) 가 Keycloak JWT 를 검증하는 최소 설정 + audience/role 매핑 customize 의 1차 근거.

Parent / 활용 branch (필수)

Branch 이 자료가 정당화하는 결정
raw/branch-notes/feature-keycloak-patterns keycloak-patterns root — Resource Server 가 issuer-uri 한 줄로 OIDC discovery → JWKS 검증을 자동 구성한다는 사실
raw/branch-notes/feature-keycloak-single-ec2-no-google P3A 단일 EC2 학습 환경 backend (application.yml) 의 spring.security.oauth2.resourceserver.jwt.issuer-uri 설정 근거
raw/branch-notes/feature-keycloak-spring-rs-audience-validator audiences property 로 aud claim 검증 추가 결정 근거 (token 이 의도된 RS 로 발급되었는지)
raw/branch-notes/feature-keycloak-spring-rs-role-mapping Keycloak realm role → Spring GrantedAuthority 매핑 시 JwtAuthenticationConverter + SCOPE_ prefix 의 default 동작 근거
raw/branch-notes/feature-keycloak-iss-claim-hostname-mismatch issuer-uri 와 token iss 가 정확 일치해야 한다는 startup expectation 의 근거 (Keycloak KC_HOSTNAME 함정)

컨텍스트

P2A/P3A 의 backend (/api/me) 는 Spring Boot 3.x + spring-security-oauth2-resource-server 로 구현. issuer-uri 한 줄로 OIDC discovery + JWKS 자동 fetch + iss 검증이 동작한다는 점이 Keycloak 통합의 최소 구성 단위. audience 검증 + 권한 추출 customize 는 keycloak realm role 매핑의 1차 근거.

출처 / Source

핵심 인용 / Key quotes (verbatim)

[§Specifying the Authorization Server] "Where idp.example.com/issuer is the value contained in the iss claim for JWT tokens that the authorization server will issue. Resource Server will use this property to further self-configure, discover the authorization server's public keys, and subsequently validate incoming JWTs."

[§Startup Expectations] "It achieves this through a deterministic discovery process it launches at the first request containing a JWT: 1. Query the Provider Configuration or Authorization Server Metadata endpoint for the jwks_url property 2. Query the jwks_url endpoint for supported algorithms 3. Configure the validation strategy to query jwks_url for valid public keys of the algorithms found 4. Configure the validation strategy to validate each JWTs iss claim against idp.example.com."

[§How JWT Authentication Works] "JwtAuthenticationProvider is an AuthenticationProvider implementation that leverages a JwtDecoder and JwtAuthenticationConverter to authenticate a JWT."

[§Configuring Authorization] "A JWT that is issued from an OAuth 2.0 Authorization Server will typically either have a scope or scp attribute, indicating the scopes (or authorities) it's been granted, for example: { …, \"scope\" : \"messages contacts\"}"

[§Configuring Authorization] "When this is the case, Resource Server will attempt to coerce these scopes into a list of granted authorities, prefixing each scope with the string "SCOPE_"."

[§Specifying the Authorization Server JWK Set Uri Directly] "If the authorization server doesn't support any configuration endpoints, or if Resource Server must be able to initialize independently from the authorization server, then the jwk-set-uri can be supplied as well"

[§Specifying the Authorization Server JWK Set Uri Directly] "Consequently, Resource Server will not ping the authorization server at startup. We still specify the issuer-uri so that Resource Server still validates the iss claim on incoming JWTs."

[§Supplying Audiences] "Boot also has the audiences property for validating the aud claim; this is who the JWT was sent to."

[§Supplying Audiences] "The result will be that if the JWT's iss claim is not idp.example.com, and its aud claim does not contain my-resource-server.example.com in its list, then validation will fail."

Claims Extracted / 추출된 주장

Claim ID Claim (이 자료가 직접 말하는 것) Evidence quote Strength Applies to Does not prove
SSRS-JWT-C1 issuer-uri property 는 token iss 값이어야 하며, Resource Server 는 이 값으로 self-configure (authorization server public key discovery + JWT 검증) [§Specifying the Authorization Server] "Where idp.example.com/issuer is the value contained in the iss claim for JWT tokens that the authorization server will issue. Resource Server will use this property to further self-configure, discover the authorization server's public keys, and subsequently validate incoming JWTs." official-vendor-doc Spring Security 6.x + OAuth2 Resource Server (servlet) issuer-uri 가 Keycloak 의 KC_HOSTNAME 과 어떻게 매핑되는지의 정확한 형태는 본 인용 범위 밖 (Keycloak 별도 문서)
SSRS-JWT-C2 startup 시 첫 JWT 요청에서 deterministic discovery 4단계 수행: (1) jwks_url 조회 (2) supported algorithms 확인 (3) JWKS 로 public key 검증 strategy 구성 (4) iss claim 을 issuer-uri 와 비교 [§Startup Expectations] "It achieves this through a deterministic discovery process it launches at the first request containing a JWT: 1. Query the Provider Configuration or Authorization Server Metadata endpoint for the jwks_url property 2. Query the jwks_url endpoint for supported algorithms 3. Configure the validation strategy to query jwks_url for valid public keys of the algorithms found 4. Configure the validation strategy to validate each JWTs iss claim against idp.example.com." official-vendor-doc Boot auto-configuration 사용 시 discovery 실패 시 retry/backoff 정책은 본 인용 범위 밖
SSRS-JWT-C3 JwtAuthenticationProviderJwtDecoder + JwtAuthenticationConverter 를 사용해 JWT 를 인증하는 AuthenticationProvider 구현체 [§How JWT Authentication Works] "JwtAuthenticationProvider is an AuthenticationProvider implementation that leverages a JwtDecoder and JwtAuthenticationConverter to authenticate a JWT." official-vendor-doc Spring Security JWT authentication chain reactive (WebFlux) 변형의 클래스명은 본 인용 범위 밖
SSRS-JWT-C4 JWT 의 scope/scp claim 의 각 scope 는 default 로 SCOPE_ prefix 가 붙은 granted authority 로 변환 [§Configuring Authorization] "When this is the case, Resource Server will attempt to coerce these scopes into a list of granted authorities, prefixing each scope with the string "SCOPE_"." official-vendor-doc default JwtAuthenticationConverter 사용 시 Keycloak realm role (realm_access.roles) 이 default 로 자동 매핑된다는 뜻은 아님 — Keycloak role 매핑은 JwtGrantedAuthoritiesConvertersetAuthoritiesClaimName customize 필요
SSRS-JWT-C5 jwk-set-uri 를 직접 지정 가능; 이 경우 startup 시 authorization server ping 안 함. 단 issuer-uri 는 여전히 명시 (token iss 검증을 위해) [§Specifying the Authorization Server JWK Set Uri Directly] "If the authorization server doesn't support any configuration endpoints, or if Resource Server must be able to initialize independently from the authorization server, then the jwk-set-uri can be supplied as well" + "Consequently, Resource Server will not ping the authorization server at startup. We still specify the issuer-uri so that Resource Server still validates the iss claim on incoming JWTs." official-vendor-doc authorization server 가 OIDC discovery 미지원 또는 RS 가 독립 부팅 요구 jwk-set-uri 단독 (issuer 없음) 사용 시 동작은 본 인용 범위 밖
SSRS-JWT-C6 Boot 의 audiences property 는 aud claim 검증을 활성화; iss 또는 aud 어느 하나라도 불일치 시 검증 실패 [§Supplying Audiences] "Boot also has the audiences property for validating the aud claim; this is who the JWT was sent to." + "The result will be that if the JWT's iss claim is not idp.example.com, and its aud claim does not contain my-resource-server.example.com in its list, then validation will fail." official-vendor-doc Boot auto-configuration + audiences property 사용 programmatic aud validator (별도 OAuth2TokenValidator) 의 정확한 추가 방식은 본 인용 범위 밖 — 별도 §Configuring Validation 페이지 참조

Usage Boundaries / 적용 경계

  • 이 자료가 직접 증명하는 것:
    • SSRS-JWT-C1: issuer-uri 한 줄로 OIDC discovery + JWKS + iss 검증이 자동화된다는 사실
    • SSRS-JWT-C2: 4단계 deterministic discovery 의 정확한 순서
    • SSRS-JWT-C3: JwtAuthenticationProvider 의 두 협력자 (JwtDecoder, JwtAuthenticationConverter) 의 정확한 명명
    • SSRS-JWT-C4: scope/scp claim → SCOPE_ prefix 자동 변환의 default 동작
    • SSRS-JWT-C5: jwk-set-uri 직접 지정 + issuer-uri 병기 패턴
    • SSRS-JWT-C6: audiences property 의 aud 검증 활성화 + 실패 조건
  • 이 자료가 증명하지 않는 것:
    • Keycloak realm_access.roles claim 이 default 로 ROLE_ prefix 의 granted authority 로 매핑된다는 뜻 — Keycloak realm role 은 scope/scp 가 아닌 별도 nested claim 이므로 JwtAuthenticationConverter customize 필수
    • opaque token introspection (별도 /oauth2/introspection 페이지)
    • issuer-uri 가 Keycloak 의 KC_HOSTNAME 변경 시 자동 추적된다는 뜻 — startup 시점에 한 번만 discovery
    • WebFlux/reactive 환경의 클래스명 (ReactiveJwtDecoder 등) 의 정확한 매핑
  • 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
    • P3A 의 KC_HOSTNAME=localhost 와 backend issuer-uri=http://localhost:8080/realms/keycloak-patterns 일치 검증 (token 의 iss 가 정확히 동일한지 token 디코딩 확인)
    • Keycloak realm role 을 ROLE_ prefix 의 authority 로 매핑하는 JwtGrantedAuthoritiesConverter 의 정확한 setAuthoritiesClaimName 값 (realm_access.roles vs resource_access.<client>.roles)
    • audiences property 가 string list 인지 single string 인지 (Boot 3.x property binding 형식)

최소 설정 (해석 — 내 프로젝트 메모)

본 섹션은 자료 직접 인용 아님. P3A 적용 가이드.

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://localhost:8080/realms/keycloak-patterns
  • 첫 요청 시 위 SSRS-JWT-C2 의 4단계 deterministic discovery 자동 수행.

JWKS URI 직접 지정 (해석)

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://localhost:8080/realms/keycloak-patterns
          jwk-set-uri: http://localhost:8080/realms/keycloak-patterns/protocol/openid-connect/certs

Audience 검증 (해석)

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://localhost:8080/realms/keycloak-patterns
          audiences: keycloak-patterns-backend

권한 추출 customize (해석)

@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
    JwtGrantedAuthoritiesConverter conv = new JwtGrantedAuthoritiesConverter();
    conv.setAuthoritiesClaimName("realm_access.roles"); // Keycloak realm role
    conv.setAuthorityPrefix("ROLE_");
    JwtAuthenticationConverter jac = new JwtAuthenticationConverter();
    jac.setJwtGrantedAuthoritiesConverter(conv);
    return jac;
}

P3A 적용 메모 (내 프로젝트 해석)

본 섹션은 자료 직접 인용 아님. P3A 결정 컨텍스트 해석.

  • issuer-uriKeycloak 이 발급한 token 의 iss 와 정확히 동일해야 함 (SSRS-JWT-C1 + C2.4) → Keycloak KC_HOSTNAME=localhost 라면 backend 도 http://localhost:8080/realms/....
  • /api/me 등 endpoint 는 .oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults())) 한 줄로 보호.
  • @AuthenticationPrincipal Jwt jwt 로 컨트롤러에서 claim 접근 → jwt.getClaimAsString("preferred_username").

한계 / 후속

  • 본 문서는 JWT validation 만. opaque token introspection 은 별도 페이지.
  • 본 wiki 변환 시 wiki/concepts/spring-security-resource-server-jwt 후보.