Method Security

Kim Hyen Su·2024년 10월 14일

대장장이

목록 보기
4/6
post-thumbnail

개요

대장장이 프로젝트를 진행하면서 인가 관련 로직을 구현하기 위해 이전에 알게되었던 Method Security 에 대해서 학습 및 정리 후 프로젝트 내 도입해볼 예정입니다.

Method Security :: Spring Security

스프링 시큐리티는 Request Level 뿐만 아니라 method level에서도 인가 관련 처리를 지원합니다.

이는 기본적으로 활성화되어 있지 않으며, 이를 활성화 시키기 위해서는 아무 @Configuration 클래스에 @EnableMethodSecurity 어노테이션을 추가해주면 됩니다.

@EnableMethodSecurity

내부 동작

method level 에서의 Security 동작을 예를 들어서 설명하겠습니다.

다음과 같이 Method Security를 사용하고 있는 서비스가 있다고 가정하겠습니다.

@Service
public class MyCustomerService {
    @PreAuthorize("hasAuthority('permission:read')")
    @PostAuthorize("returnObject.owner == authentication.name")
    public Customer readCustomer(String id) { ... }
}

위 코드의 동작을 자세하게 보면, 다음과 같습니다.

  1. Spring AOP는 readCustomer의 프록시 메서드를 호출합니다. 프록시의 advisors 중 @PreAuthorize 포인트컷과 일치하는 AuthorizationManageBeforeMethodInterceptor를 호출합니다.
  2. 위 인터셉터는 PreAuthorizeAuthorizationManger#check 메서드를 호출합니다.
  3. 위 매니저는 MethodSecurityExpressionHandler를 통해 어노테이션에 SpEL expression, (예시) “hasAuthority(’permission:read’)”을 파싱합니다. 그리고 Supplier과 MethodInvocation을 포함하는 MethodSecurityExpressionRoot로 부터 EvaluationContext에 해당하는 부분을 구성합니다.
  4. 위 인터셉터(AuthorizationManageBeforeMethodInterceptor)는 위 컨텍스트를 사용하여 표현식을 평가합니다. 구체적으로, 인터셉터는 표현식을 읽고 컬렉션에 permission:read가 포함되어 있는지 확인합니다.
  5. Evaluation을 통과한다면, Spring AOP에서는 그 메서드를 호출합니다.
  6. 통과하지 못했다면, 인터셉터는 AuthorizationDeniedEvent 임을 알립니다. 그리고 403 응답 상태코드와 함께 AccessDeniedException 예외를 던집니다.
  7. 메서드의 반환 후, Spring AOP는 @PostAuthorize 포인트컷과 일치하는 AuthorizationManagerAfterMethodInterceptor를 호출합니다. 동작은 이전과 거의 동일하지만, PostAuthorizeAuthorizationManager 에 의해 메서드가 호출된다는 점이 다릅니다.

특징

  • 여러개의 어노테이션은 순차적으로 실행됩니다.
  • 동일한 어노테이션을 반복하여 사용 불가합니다.
  • 각 어노테이션은 그들 전용의 Method Interceptor가 존재합니다.
  • 복잡한 권한 부여에 대한 처리가 가능합니다.
    • RoleHierarchy를 활용하여 하나의 권한에서 다른 권한까지 함께 접근이 가능하도록 설정할 수 있습니다.
    • 예를 들면, 관리자 회원은 일반 회원이 읽기 접근이 가능한 자원에 접근이 가능할 수 있다라는 요구사항이 있을 경우 다음과 같이 정의가 가능합니다.
      @PreAuthorize("hasAuthority('permission:read') || hasAuthority('ADMIN')")
      • permission:read 권한이 있거나 ADMIN role을 가진 회원은 해당 자원에 접근이 가능합니다.

      • 하지만, 이를 RoleHierachy를 활용하게 되면 간단하게 표현할 수 있습니다.

        @Bean
        static RoleHierarchy roleHierarchy() {
            return RoleHierarchyImpl.fromHierarchy("ROLE_ADMIN > permission:read");
        }
      • 위 설정을 통해서 다음과 같이 설정할 수 있습니다.

        @PreAuthorize("hasAuthority('permission:read')")

Request-level vs. Method-level

둘을 비교한 간단한 표가 공식 문서에 명시되어 있습니다. request-level에서의 권한 처리가 조금 더 러프하게 처리하고, method-level이 조금 더 세밀하게 권한 부여가 가능합니다. config 위치는 request-level은 config 클래스 내부에 정의하고, method-level은 메서드 상단에 정의합니다. request-level은 DSL을 사용하고, method-level은 어노테이션을 사용하고, 정의는 request는 programmatic, method는 Spring Expression Language를 사용합니다.

권한 어노테이션

  • 권한 어노테이션들은 메서드 뿐만아니라 클래스 그리고 인터페이스 레벨에서도 사용이 가능합니다.
  • PreAuthorize : authorities를 검증 시 유용한 어노테이션입니다. 메서드 호출 전에 검증이 수행됩니다.
  • PostAuthroize : 메서드 호출 후 결과에 대한 검증이 수행됩니다.
  • PreFilter : 메서드 호출 전에 필터링이 수행됩니다. filterObject를 활용하여 메서드 요청 시 들어오는 계정을 의미하며, 모든 계정을 일일히 처리합니다.
  • PostFilter : 메서드 호출 후 결과를 필터링 합니다. filterObject는 계정을 의미하며, 모든 계정을 일일히 처리합니다.
  • Secured : PreAuthorize 이전 버전에 사용했던 레거시 옵션입니다.

내부 SpEL 식

  • permitAll - 해당 자원에 누구나 접근이 가능합니다.
  • denyAll - 해당 자원에 누구든 접근이 불가능합니다.
  • hasAuthority - 해당 자원에 접근하기 위해서는 GrantAuthority에 일치하는 value가 있어야 접근 가능합니다.
  • hasRole - hasAuthority 와 유사한 기능을 한다. 차이점은 value 확인 시 접두사로 ROLE_ 을 자동으로 붙여줍니다.
  • hasAnyAuthority - 해당 자원에 접근하기 위해서는 GrantAuthority에 일치하는 value가 있어야 접근 가능합니다.
  • hasAnyRole - hasAnyAuthority 와 유사한 기능을 한다. 차이점은 value 확인 시 접두사로 ROLE_ 을 자동으로 붙여줍니다.
  • hasPermission - PermissionEvaluator 객체의 동작을 위한 어노테이션입니다.

마무리

위처럼 간단하게 구현에 필요한 일부 내용들만 공식문서에서 찾아서 정리해보았습니다. 관련해서 조금 더 Deep 하게 학습이 필요하다면, 공식문서를 참고하여 차차 추가해 나갈 예정입니다.

profile
백엔드 서버 엔지니어

0개의 댓글