Files
llm-wiki/raw/blog-topics/framework-free-method-authorization-clean-architecture-2026-06-08.md

95 lines
4.8 KiB
Markdown

---
title: blog-topic / Spring Security 없이 application layer 에 method-level 인가 걸기
source_type: blog-topic
status: raw
related_branches: [feature-authentication-authorization-contract]
related_projects: [ca-tmpl]
tags: [blog-topic, ca-tmpl, security, authorization, clean-architecture, spring-security, method-security]
created: 2026-06-08
status_label: ready-for-canonical
target_audience: backend-engineer
inspiration_url:
archive_url:
---
# blog-topic: framework-free method authorization in Clean Architecture
> Layer: `raw/blog-topics/` — 구현에서 나온 블로그 글감 seed. canonical 추출은 `/ingest` 시 별도.
## Parent / 부모
- [[raw/branch-notes/feature-authentication-authorization-contract]]
## 글감 한 줄
"`@PreAuthorize` 를 쓰면 application layer 가 Spring Security 에 결합된다. annotation 은 core 에, 집행은 adapter 에 두면 layer 순수성을 지키면서 method-level 인가를 걸 수 있다."
## 핵심 논지 / Outline
1. **문제**: Clean Architecture 에서 application/domain 은 framework-free 여야 하는데, Spring method security(`@PreAuthorize`/`@Secured`)는 bean 을 `org.springframework.security` 에 결합시킨다.
2. **분리**: *결정(decision)**메커니즘(mechanism)* 분리.
- core: `@RequiresPermission`(plain annotation) + `AuthorizationPort`(plain interface) + `Permission`(value object) + `AuthorizationPrincipal`(raw roles).
- adapter: `AuthorizationManager<MethodInvocation>` 가 annotation 을 읽고 principal 을 매핑해 port 에 위임. `@EnableMethodSecurity(prePostEnabled=false)` + custom `Advisor`(ROLE_INFRASTRUCTURE) 로 wiring.
3. **거부 → 403 의 2-hop**: core 가 자체 예외 throw → adapter 가 Spring `AccessDeniedException` 으로 변환 → 에러 envelope.
4. **permission-centric RBAC**: role=permission 묶음, registry 로 raw role→effective permission 해소(case-insensitive, fail-closed, no wildcard=least-privilege).
5. **함정**: CGLIB vs JDK proxy(concrete 주입 시 `proxyTargetClass=true` 필수), AOP self-invocation bypass, unauthenticated(`AuthenticationException`) vs unauthorized(`AccessDeniedException`) 구분. → [[raw/errors/method-security-cglib-vs-jdk-proxy-usecase-injection-2026-06-08]]
6. **마이그레이션 path**: port interface 덕분에 RBAC→ABAC 전환이 구현체 교체로 끝남.
## 차별점
대부분의 Spring 튜토리얼은 `@PreAuthorize` 를 service 에 바로 붙인다. 이 글은 "왜 그게 hexagonal/clean 구조에서 부채인가 + 어떻게 분리하나"를 코드(TransactionPort 선례와 동일 패턴)로 보여준다.
## 트리거 / Trigger
- 트리거 유형: `branch-work`
- 트리거 날짜: 2026-06-08
- 트리거 연결 노트: [[raw/branch-notes/feature-authentication-authorization-contract]]
## 글감 / Topic seed
- 한 문장 요지: application layer에 Spring Security annotation을 직접 붙이지 않고 plain annotation + port + adapter method-security로 method-level authorization을 구현한다.
- 예상 제목 후보:
- Clean Architecture에서 framework-free method authorization 만들기
- `@PreAuthorize` 없이 application layer 인가 걸기
## 핵심 주장 후보 / Claim candidates
- 사실 후보:
- core annotation과 authorization port는 Spring Security type을 직접 의존하지 않는다.
- adapter가 Spring `AuthorizationManager`와 advisor wiring을 소유한다.
- 의견/해석 후보:
- authorization decision과 framework mechanism을 분리하면 RBAC→ABAC migration path가 단순해진다.
## Outline seed
1. `@PreAuthorize`가 application layer purity를 깨는 경로를 설명한다.
2. core annotation/port와 adapter enforcement를 분리한다.
3. 403 envelope, CGLIB/JDK proxy, self-invocation bypass 한계를 적는다.
## Canonical 전환 후보 / Canonical extraction candidates
- `wiki/projects/ca-tmpl/security-baseline-jwt-actuator-secrets.md` 후보:
- framework-free method authorization 글감.
- 필요한 추가 검증:
- 현재 `@RequiresPermission`, `AuthorizationPort`, method security adapter 구현 여부.
## Sources / 근거 후보
- [[raw/branch-notes/feature-authentication-authorization-contract]]
- [[raw/errors/method-security-cglib-vs-jdk-proxy-usecase-injection-2026-06-08]]
## 미해결 / Unknown
- 아직 확인해야 할 사실: method security custom advisor의 현재 구현·검증 여부.
- 과장하면 안 되는 부분: Spring Security 자체를 부정하지 않고 ca-tmpl layer boundary 선택으로 제한한다.
## Decision / 처리 결정
- 액션: `promote-to-canonical`
- 이유: `wiki/projects/ca-tmpl/security-baseline-jwt-actuator-secrets.md` 에 framework-free method authorization 글감으로 반영한다.
- 다음 단계: blogify 전 구현 증거와 proxy/self-invocation 한계를 확인한다.
## Related / 관련
- 관련 branch: [[raw/branch-notes/feature-authentication-authorization-contract]]