[Spring Security] 인가 아키텍처
🔐 스프링 시큐리티
🏛️ GrantedAuthority
사용자(principal, 주체)에게 부여된 권한을 나타내는 인터페이스이다.
모든 Authentication 구현체들은 GrantedAuthority 타입 객체들의 리스트를 가지고 있다. (Authentication 코드 부분 참고)
GrantedAuthority 객체들은 AuthenticationManager에 의해 Authentication 객체에 삽입된다.
SimpleGrantedAuthority는 GrantedAuthority의 구현체로, 대부분의 AuthenticationProvider 구현체들은 SimpleGrantedAuthority를 사용해 Authentication를 채운다.
기본적으로 역할 기반의 인가 규칙(role-based authorization)은 역할 앞에 ROLE_를 접두사로 사용한다. 예를 들어, 보안 컨텍스트가 “USER” 역할을 필요로 하는 인가 규칙이 있다면 스프링 시큐리티는 기본적으로 GrantedAuthority#getAuthority가 “ROLE_USER”를 반환하는지 확인한다.
접두사는 GrantedAuthorityDefaults를 사용해 커스텀 할 수 있다. 이부분은 공식문서를 확인하자.
👮 AuthorizationManager
AuthorizationManager 인터페이스는 스프링 시큐리티의 요청 기반, 메소드 기반, 메시지 기반의 인가 구성 요소에서 호출되며, 접근 제어 결정을 내리는 최종 책임자이다. 이전에 사용되던 AccessDecisionManager와 AccessDecisionVoter를 대체한다.
주요 메서드:
check: 접근 허용 여부를 결정한다- 인가 결정을 내리는 데 필요한 모든 정보(인증 객체와 보안 대상 객체(예: 권한 정보, 요청 정보, 호출 정보 등))를 입력받아, 접근이 허용되는지 여부를 판단한다.
- 반환 값은
AuthorizationDecision객체로, 접근 허용(true), 접근 거부(false), 결정 유보(null) 중 하나이다. - 예시: 메서드 호출에 대한 보안을 검사할 때, 메서드의 인자를 검사하여 현재 사용자가 그 작업을 수행할 권한이 있는지 확인할 수 있다.
verify:check메서드를 호출해서 접근 허용 여부를 검사한 후, 만약 접근이 거부되면AccessDeninedException을 던진다.
요약하자면, AuthorizationManager는 스프링 시큐리티에서 “이 사용자가 여기에 접근할 수 있나요?”라는 질문에 대한 대답(접근 제어 결정)을 한다.
🎭 위임 기반 AuthorizationManager 구현체
커스텀 AuthorizationManager를 통해서도 인가 과정을 제어할 수 있지만, 스프링 시큐리티에서 기본적으로 제공하는 위임 AuthorizationManager를 사용하면 여러 AuthorizationManager와 협력해서 관리할 수 있다.

RequestMatcherDelegatingAuthorizationManager는 요청을 가장 적절한 AuthorizationManager와 매칭시켜 준다.
메소드 보안(Method Security)에서는 AuthorizationManagerBeforeMethodInterceptor와 AuthorizationManagerAfterMethodInterceptor를 사용할 수 있다.
| 구현체 | 특징 |
|---|---|
AuthorityAuthorizationManager |
특정 권한을 가진 사용자에게만 접근을 허용한다. 주로 사용자의 권한(예: ROLE_USER, ROLE_ADMIN)을 기반으로 접근을 제어한다. |
AuthenticatedAuthorizationManager |
인증된 사용자에게 접근을 허용한다. 이 클래스는 사용자가 시스템에 로그인했는지 여부를 기준으로 결정한다. |
WebExpressionAuthorizationManager |
웹 보안 표현식을 사용하여 권한을 관리한다. 예를 들어, “hasRole(‘ADMIN’)” 또는 “hasAuthority(‘WRITE_PERMISSIONS’)”과 같은 표현식을 사용할 수 있다. |
RequestMatcherDelegatingAuthorizationManager |
인가 설정에서 지정한 모든 요청 패턴과 권한 규칙을 매핑한 정보를 가지고 있으며, 권한 검사 시 가장 적합한 AuthorizationManager 구현체를 선택해 위임한다. |
PreAuthorizeAuthorizationManager |
메소드 실행 전에 권한을 검사한다. @PreAuthorize 어노테이션과 함께 사용되며, 메소드 실행 전에 사용자의 권한을 확인한다. |
PostAuthorizeAuthorizationManager |
메소드 실행 후에 권한을 검사한다. @PostAuthorize 어노테이션과 함께 사용되며, 메소드 실행 후 결과에 따라 접근을 허용하거나 거부한다. |
Jsr250AuthorizationManager |
JSR-250 어노테이션(@RolesAllowed, @DenyAll, @PermitAll)을 사용하여 권한을 관리한다. |
SecuredAuthorizationManager |
@Secured 어노테이션을 사용하여 메소드 수준의 보안을 제공한다. 이 어노테이션은 특정 권한을 가진 사용자만 메소드에 접근할 수 있게 한다. |
🗂️ 계층적 역할 (Hierarchical Roles)
많은 애플리케이션에서 사용자는 단일 역할만을 갖는 것이 아니라, 여러 역할이 계층적으로 구성되어 있는 경우가 많다. (“admin”과 “user” 역할이 있는 애플리케이션에서 “admin”에게 “user”의 역할도 함께 할당해야 하는 경우 등) 이때 계층적 역할(Hierarchical Roles)을 활용하면 보안 관리가 훨씬 간편해진다.
역할 계층 구성 방법
Java 코드와 XML 코드로 구성할 수 있는데, 여기서는 자바 코드만 보여주겠다. XML 코드는 공식문서를 참고하자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Bean
static RoleHierarchy roleHierarchy() {
return RoleHierarchyImpl.withDefaultRolePrefix()
.role("ADMIN").implies("STAFF")
.role("STAFF").implies("USER")
.role("USER").implies("GUEST")
.build();
}
// pre-post 메서드 보안도 사용하는 경우 추가로 설정
@Bean
static MethodSecurityExpressionHandler methodSecurityExpressionHandler(RoleHierarchy roleHierarchy) {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setRoleHierarchy(roleHierarchy);
return expressionHandler;
}
위 설정에서 ROLE_ADMIN은 ROLE_STAFF를 포함하며, ROLE_STAFF는 ROLE_USER를 포함하고, ROLE_USER는 ROLE_GUEST를 포함하는 구조이다. 따라서 ROLE_ADMIN으로 인증된 사용자는 모든 하위 역할인 ROLE_STAFF, ROLE_USER, ROLE_GUEST를 가진 것으로 간주된다.
🦖 AccessDecisionManager와 AccessDecisionVoter
AuthorizationManager는 Spring Security 5.5부터 도입된 새로운 인가 결정 인터페이스이다. AccessDecisionManager와 AccessDecisionVoter를 대체해서 얻은 장점 설명은 추후 서술하겠다.
참고
Leave a comment