본문 바로가기
Spring

Spring Security - @PreAuthorize, @PostAuthorize

by irerin07 2024. 1. 16.
728x90

Method Security 동작방식


Method Security동작 방식

  • 스프링 시큐리티에서 제공하는 Method Security는 다음과 같은 상황에서 유용할 수 있습니다.
    • 세분화된 Authorization 로직 추출 : 예를 들자면, 메서드 파라미터나 반환 값이 Authorization에 영향을 미치는 경우
    • 서비스 레이어에서 보안을 강제하고자 하는 경우
    • HttpSecurity기반 설정대신 애노테이션 기반으로 처리하고자 하는 경우
  • Method Security는 스프링 AOP를 사용해 만들어졌기 때문에 필요한 경우 스프링 시큐리티 기본값을 재정의(Override) 하여 사용할 수 있습니다.
💡 `@EnableMethodSecurity`는 Deprecated된 `@EnableGloablMethodSecurity`를 대체하는데 다음과 같은 개선점을 가지고 있습니다. 1. `Metadata sources`, `config attributes`, `decision managers`, 그리고 `voters`를 사용하는 대신 단순화된 `AuthorizationManager`를 사용합니다. 이는 재사용과 커스터마이징을 좀 더 간편하게 할 수 있도록 해줍니다. 2. Bean을 커스터마이징 하기 위해 `GlobalMethodSecurityConfiguration`을 extend할 필요 없이 직접적인 빈 기반 구성을 제공합니다. 3. 스프링 AOP를 사용해 만들어졌습니다. 추상화를 제거하고 Spring AOP building block을 사용하여 커스터마이징이 가능합니다. 4. JSR-250을 준수합니다. 5. 기본적으로 `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`,`@PostFilter` 를 사용할 수 있습니다.
  • Method Security는 메소드 전-후 authorization의 조합입니다.
@Service
public class MyCustomerService {
    @PreAuthorize("hasAuthority('permission:read')")
    @PostAuthorize("returnObject.owner == authentication.name")
    public Customer readCustomer(String id) { ... }
}
  • Method Security가 설정 된 MyCustomerService의 readCustomer 메소드를 호출했을때 내부적으로 발생하는 흐름은 다음과 같습니다.

출처: https://docs.spring.io/spring-security/reference/servlet/authorization/method-security.html#migration-enableglobalmethodsecurity

  1. Spring Aop가 우선 readCustomer의 프록시 메서드를 호출합니다. 프록시의 어드바이저들 중에 @PreAuthorize의 포인트컷과 매치되는 AuthorizationManagerBeforeMethodInterceptor를 호출합니다.
  2. AuthorizationManagerBeforeMethodInterceptorPreAuthorizeAuthorizationManagercheck를 호출합니다.
  3. PreAuthorizeAuthorizationManagerMethodSecurityExpressionHandler 를 사용하여 @PreAuthorize 애노테이션의 SpEL을 분석하고, MethodSecurityExpressionRoot를 사용해 Supplier과 MethodInvocation을 포함하고 있는 EvaluationContext를 생성한다.
  4. AuthorizationManagerBeforeMethodInterceptorEvaluationContext를 사용하여 Expression을 체크합니다.. 구체적으로는, Supplier에서 Authentication을 읽어온 뒤, authorities에 permission:read를 포함하고 있는지 확인합니다.
  5. 체크가 통과되면 Spring AOP가 메소드 호출을 진행합니다.
  6. 만약 통과하지 못했다면, AuthorizationManagerBeforeMethodInterceptorAuthorizationDeniedEvent 를 발생시키고 AccessDeniedException을 throw 합니다. 이 Exception은 ExceptionTranslationFilter에서 잡히고, 403 상태코드를 응답으로 반환합니다.
  7. readCustomer 메서드가 값을 반환한 뒤, Spring AOP는 @PostAuthorize의 포인트컷과 매치되는 AuthorizationManagerAfterMethodInterceptor를 호출합니다. 위에서 설명한 방식과 동일하게 진행되지만 PreAuthorizeAuthorizationManager 대신 PostAuthorizationManager를 사용합니다.
  8. 만약 체크가 통과하면(예시 코드의 경우 반환 값이 logged-in-user의 값이라면) 정상적으로 나머지 프로세스를 진행합니다.
  9. 만약 통과하지 못했다면, AuthorizationDeniedEvent를 발생시키고, AccessDeniedException을 throw 합니다. 이 역시 ExceptionTranlsationFilter에서 잡혀, 403 상태코드를 응답으로 반환합니다.
728x90