11 KiB
title, source_type, url, archive_url, related_branches, related_projects, tags, status, confidence, created, last_reviewed
| title | source_type | url | archive_url | related_branches | related_projects | tags | status | confidence | created | last_reviewed | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Extract roles from access token issued by Keycloak using Spring Security (Between Data / Christian Huff) | personal-blog | https://betweendata.io/posts/secure-spring-rest-api-using-keycloak/ |
|
|
|
raw | medium | 2026-07-18 | 2026-07-18 |
Extract roles from access token issued by Keycloak using Spring Security (Between Data / Christian Huff)
Layer:
raw/company-tech-blogs/(분류: 실제source_type은personal-blog— 저자 Christian Huff 개인 블로그. 저장소 기존 관행(raw/company-tech-blogs/test-pyramid-vs-trophy-kent-dodds,senior-engineer-competency-mubin-shaikh.md,deliberate-practice-software-developers-redgreencode.md)에 따라company-tech-blogs/디렉토리에 위치하되 frontmattersource_type: personal-blog유지). 회사 기술 블로그가 아니므로 공식 best practice 로 격상 금지 (CLAUDE.md §5, §11). 아래 모든 Claim 은engineering-blog강도 — 개인 저자의 구현 사례일 뿐, Spring/Keycloak 공식 권고가 아니다.
Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| raw/branch-notes/feature-keycloak-spring-rs-role-mapping | Keycloak realm role 을 Spring GrantedAuthority 로 매핑하는 구현 방식 — 손으로 작성한 Converter<Jwt, Collection<GrantedAuthority>> 가 nested realm_access claim 을 읽어 ROLE_ prefix 붙은 authority 로 변환하고, 필요 시 DelegatingJwtGrantedAuthoritiesConverter 로 default scope converter 와 결합하는 접근의 사례(case study) 근거. feature-keycloak-spring-rs-role-mapping D4 (realm role 만 매핑) 의 구현 detail 참고 자료. |
출처 / Source
- 원본 URL: https://betweendata.io/posts/secure-spring-rest-api-using-keycloak/
- 아카이브 URL: (미수집)
- 저자 / 조직: Christian Huff (개인 블로그 "Between Data")
- 발행일: 2023-02-23
- 마지막 확인일: 2026-07-18
왜 저장했는지 / Why archived
Spring Boot 3 + spring-boot-starter-oauth2-resource-server 환경에서 Keycloak 이 발급한 JWT 의 realm_access/resource_access claim 은 Spring 의 기본 JwtGrantedAuthoritiesConverter 가 자동으로 추출하지 못한다. 이 자료는 그 문제를 커스텀 Converter<Jwt, Collection<GrantedAuthority>> 로 해결한 구체 코드 사례를 담고 있어, feature-keycloak-spring-rs-role-mapping 의 role mapping 구현 detail 을 정당화하는 참고 사례로 보관.
핵심 인용 / Key quotes (verbatim)
[§Extract Roles from Access Token — class declaration] "public class KeycloakJwtRolesConverter implements Converter<Jwt, Collection> {"
[§Extract Roles from Access Token — realm_access claim 이름 정의 + 실제 읽기] "private static final String CLAIM_REALM_ACCESS = "realm_access";" [...] "Map<String, Collection> realmAccess = jwt.getClaim(CLAIM_REALM_ACCESS);"
[§Extract Roles from Access Token — ROLE_ prefix 상수] "public static final String PREFIX_REALM_ROLE = "ROLE_realm_";" [...] "public static final String PREFIX_RESOURCE_ROLE = "ROLE_";"
[§Extract Roles from Access Token — ROLE_ prefix 설명 (본문)] "In the returned authorities the realm roles are prefixed with ROLE_realm_ while the resource roles are prefixed with ROLE_[NAME_OF_THE_RESOURCE]_."
[§Define Access Rules — DelegatingJwtGrantedAuthoritiesConverter 조합] "new DelegatingJwtGrantedAuthoritiesConverter(" [...] "new JwtGrantedAuthoritiesConverter()," [...] "new KeycloakJwtRolesConverter());"
Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| KC-ROLE-BD-C1 | 저자는 Converter<Jwt, Collection<GrantedAuthority>> 를 구현하는 KeycloakJwtRolesConverter 클래스를 작성해, realm_access claim 이름을 상수로 정의하고 jwt.getClaim(CLAIM_REALM_ACCESS) 로 직접 읽는다 |
"public class KeycloakJwtRolesConverter implements Converter<Jwt, Collection> {" / "private static final String CLAIM_REALM_ACCESS = "realm_access";" / "Map<String, Collection> realmAccess = jwt.getClaim(CLAIM_REALM_ACCESS);" | engineering-blog |
Spring Boot 3 + Spring Security OAuth2 Resource Server 환경에서 Keycloak realm_access (nested claim map) 을 GrantedAuthority 로 변환하는 구현 패턴 |
이 방식이 Spring 또는 Keycloak 공식 권고 패턴이라는 것은 아님 (원문에 공식 문서 인용 없음). Keycloak 모든 버전에서 realm_access claim 구조가 동일하다는 보증도 아님 — 원문 예시 토큰은 특정 시점(2023-02) Keycloak 버전 기준 |
| KC-ROLE-BD-C2 | realm-level role 은 ROLE_realm_ prefix, resource(client)-level role 은 ROLE_[리소스명]_ prefix 를 붙여 SimpleGrantedAuthority 로 변환한다고 명시 |
"public static final String PREFIX_REALM_ROLE = "ROLE_realm_";" / "public static final String PREFIX_RESOURCE_ROLE = "ROLE_";" / "In the returned authorities the realm roles are prefixed with ROLE_realm_ while the resource roles are prefixed with ROLE_[NAME_OF_THE_RESOURCE]_." | engineering-blog |
Spring Security hasAuthority(...) 매칭을 위한 authority 명명 규칙의 한 예시(개인 저자 관례) |
ROLE_ prefix 가 Spring Security 의 필수 요구사항이라는 것은 아님 — hasAuthority 는 임의 문자열 매칭이 가능하고, ROLE_ prefix 규칙은 hasRole(...) 사용 시에만 Spring 이 자동으로 붙이는 것과는 다른 맥락(원문은 이 구분을 설명하지 않음) |
| KC-ROLE-BD-C3 | WebSecurityConfiguration.filterChain(...) 에서 DelegatingJwtGrantedAuthoritiesConverter 를 사용해 default JwtGrantedAuthoritiesConverter 와 커스텀 KeycloakJwtRolesConverter 를 함께 등록한다 |
"new DelegatingJwtGrantedAuthoritiesConverter(" [...] "new JwtGrantedAuthoritiesConverter()," [...] "new KeycloakJwtRolesConverter());" | engineering-blog |
scope 기반 default authority 와 realm/resource role 기반 custom authority를 하나의 authorities 집합으로 합치는 조합 패턴의 사례 | 이 조합이 모든 프로젝트에 필요하다는 것은 아님 — scope 기반 인가를 병행하지 않는 프로젝트라면 default converter 생략 가능. 원문도 코드 주석 수준("Using the delegating converter multiple converters can be combined")의 설명만 제공하며 DelegatingJwtGrantedAuthoritiesConverter API 계약 자체의 공식 문서화는 아님 |
참고: 이 raw 는 engineering-blog 강도만 제공 — official 보강 필요
KC-ROLE-BD-C1~C3 는 모두 engineering-blog (개인 저자 사례). branch-note 에서 이를 "공식 best practice" 로 인용하면 안 된다 (CLAUDE.md §5, §11). realm_access 가 default JwtGrantedAuthoritiesConverter 로 자동 매핑되지 않는다는 사실 자체의 공식 근거가 필요하면 raw/official-docs/spring-security-resource-server-jwt (예: 기존 branch-note 인용 SSRS-JWT-C4 — default converter 는 scope/scp 만 SCOPE_ prefix 로 자동 변환) 를 함께 인용해야 official-vendor-doc 급 근거가 된다. 이 raw 단독으로는 D4(realm role 만 매핑) 의 "왜 커스텀 컨버터가 필요한가"에 대한 사례일 뿐, "Spring 이 이렇게 하라고 권고한다"는 근거는 아니다.
Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
KC-ROLE-BD-C1: 손으로 작성한Converter<Jwt, Collection<GrantedAuthority>>구현이realm_accessclaim 을 nested map 으로 읽어올 수 있다는 동작 사례 (저자 GitHub 리포지토리에 테스트 100% 커버리지 존재한다고 원문이 주장 — 코드 자체는 미검증)KC-ROLE-BD-C2:ROLE_realm_/ROLE_[resource]_prefix 부여 방식 예시KC-ROLE-BD-C3:DelegatingJwtGrantedAuthoritiesConverter로 default + custom converter 를 합치는 코드 구조 예시
- 이 자료가 증명하지 않는 것:
- 이 구현이 Spring Security 또는 Keycloak 의 공식 권장 패턴이라는 명제 — 원문은 개인 저자의 "minimally invasive" 선택 설명일 뿐, RFC/공식 문서 인용 없음
realm_access.roles매핑이 모든 Keycloak 버전·모든 client 설정에서 동일하게 동작한다는 명제 — 예시 토큰은 특정 realm/client 설정(backendrealm,rest-apiclient) 기준ROLE_prefix 없이hasAuthority/hasRole을 섞어 쓸 때의 Spring Security 내부 동작 차이에 대한 설명 — 원문 미포함
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
feature-keycloak-spring-rs-role-mapping이 실제로resource_access(client-level role) 까지 매핑할지, 아니면 D4 결정대로realm_access만 매핑할지 — 이 raw 의KeycloakJwtRolesConverter는 두 claim 을 모두 처리하므로 branch 결정과 범위가 다름(branch 는 realm role만, 이 raw 는 realm+resource 모두)에 주의- 로컬 Keycloak 인스턴스에서 발급한 access token 의
realm_access.roles실제 JSON 구조가 이 raw 의 예시 토큰과 일치하는지 확인 DelegatingJwtGrantedAuthoritiesConverter조합이feature-keycloak-spring-rs-role-mapping의 범위(§구현 가이드)에 실제로 필요한지 — branch 는@PreAuthorize대신 SecurityFilterChain matcher 를 우선하기로 결정했으므로 (D5), 이 raw 의.requestMatchers(...).hasAuthority(...)패턴과의 정합 재검토 필요
메모 / Notes
검증되지 않은 내 해석은 wiki source-summary 단계에서만.
- 원문은 realm-level role 과 resource(client)-level role 을 모두 매핑하는 구현(
KeycloakJwtRolesConverter)을 제시하지만,feature-keycloak-spring-rs-role-mapping의 D4 는 "realm role만 매핑 (resource_access 무시)"로 범위를 좁혔다 — 이 raw 를 인용할 때 resource_access 부분은 branch 범위 밖임을 명시해야 함 (OUT_OF_BRANCH_SCOPE 유사 주의). - 원문 저자는 Keycloak 기본 설정(mapper 미변경)을 유지하는 쪽을 "minimally invasive" 라고 표현 — 이는 branch 의 "Keycloak mapper 커스터마이징 대신 Spring 쪽 컨버터로 흡수" 방향과 같은 트레이드오프 축으로 보인다(해석, 미검증).
- 저자는 GitHub 코드 링크(
ChristianHuff-DEV/secure-spring-rest-api-using-keycloak)와 100% 테스트 커버리지를 주장하나, 이 raw 는 블로그 본문만 발췌·검증했고 GitHub 코드 자체는 self-grep 대상에 포함하지 않음 — 실제 사용 시 코드 diff 재확인 필요.
Related / 관련
- 같은 주제 다른 raw:
- raw/official-docs/spring-security-resource-server-jwt — default
JwtGrantedAuthoritiesConverter가scope/scp만 자동 매핑한다는 공식 근거 (SSRS-JWT-C4) — 이 raw 의 C1과 짝을 이뤄야official-vendor-doc급 근거 완성
- raw/official-docs/spring-security-resource-server-jwt — default
- 인용하는 branch:
- 인용한 wiki 요약: (미작성)