--- 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` 가 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]]