Spring Security - 인가 처리 (방법)

이유석·2024년 11월 22일
1

Spring-Security

목록 보기
7/10
post-thumbnail

출처

HttpServletRequest 인가

  • Spring Security는 요청 레벨에서 인가 처리를 할 수 있도록 해준다.
    ex. /admin 에 대한 요청은 인증처리가 되있어야 한다.
  • 기본적으로, Spring Security는 모든 요청이 authenticated(인증되있어야 함)임을 요구한다.
    즉, 언제든 인가 규칙을 적용하기 위해서 HttpSecurity 인스턴스를 사용할 수 있다는 것이다.
     @Bean
     public SecurityFilterChain web(HttpSecurity http) throws Exception {
     	http
     	    .authorizeHttpRequests((authorize) -> authorize
     	        .anyRequest().authenticated()
     	    )
     	    // ..
     	return http.build();
     }
  • HttpServletRequest 에 대한 인가 처리시 아래의 메서드를 사용 가능하다.
    • authenticated() : 인증된 모든 사용자만 접근할 수 있도록 설정할 때 사용됩니다.
    • permitAll() : 모든 사용자(인증되지 않은 사용자 포함)가 접근할 수 있도록 허용합니다.
    • denyAll() : 모든 요청을 거부합니다. → 특정 리소스에 대해 접근을 차단할 때 사용됩니다.
    • hasRole(String role) : 사용자가 특정 역할(ROLE_)을 가지고 있는지 확인합니다.
    • hasAuthority(String authority) : 사용자가 특정 권한을 가지고 있는지 확인합니다.
    • anyOf() : 전달된 여러 AuthorizationManager 중 하나만 만족하면 접근을 허용합니다.
    • allOf() : 전달된 모든 AuthorizationManager 조건을 만족해야 접근을 허용합니다.
    • custom(Predicate) : 커스텀 로직을 기반으로 동작하는 인가 매니저를 정의할 수 있습니다.
      ex. 요청에 특정 헤더가 포함되어야만 접근 가능하도록 설정할 때.

Request Authorization Components 작동 원리

  1. AuthorizationFilterSecurityContextHolder에서 Authentication을 추출하고 처리하는 Supplier를 매개변수로 갖고 생성된다.
  2. AuthorizationFilterAuthorizationManager에게 Supplier<Authentication>HttpServletRequest 를 전달한다.
    AuthorizationManager 는 요청을 authorizeHttpRequests 에 있는 패턴과 일치시키고 아래의 규칙을 수행한다.
    1. 인가가 반려될 경우 → AuthorizationDeniedEvent 를 발행한다. 그리고 AccessDeniedException 예외가 던져진다. ExceptionTranslationFilterAccessDeniedException 을 처리하게 된다.
    2. 인가가 승인될 경우 → AuthorizationGrantedEvent 가 발행된다. 그리고, AuthorizationFilterFilterChainFilter 를 계속 진행한다.

AuthorizationFilter 는 기본적으로 Spring Security FilterChain의 마지막이다.

  • Spring Security 의 인증 필터를 포함한 다른 필터들은 인가 처리가 수행되지 않는다.
  • 만약, 커스텀 필터를 AuthorizationFilter 이전에 추가시틴다면, 이 필터 역시 인가 처리가 수행되지 않는다.

Method Security

  • 또한, Spring Security는 메서드 레벨에서 인가 처리를 할 수 있도록 해준다.

  • Method Security 를 활성화 하기 위해서, 설정 파일 (@Configuration 클래스)에 @EnableMethodSecurity 를 추가해주어야 한다.

  • 이제 @PreAuthorize, @PostAuthorize, @PreFilter, @PostFilter을 Spring에서 관리하는 클래스나 메서드에 추가하여 입력 매개변수와 반환 값을 포함한 메서드 호출에 대해서 인가처리를 할 수 있습니다.

    • 각 인가 처리가 실패하면, AccessDeniedException 예외가 던져집니다.
  • @PreAuthorize

    • 사용자가 ADMIN 역할을 갖고 있는 경우에만 해당 메서드가 호출됩니다.

      @Component
      public class BankService {
      	@PreAuthorize("hasRole('ADMIN')")
      	public Account readAccount(Long id) {
              // ... is only invoked if the `Authentication` has the `ROLE_ADMIN` authority
      	}
      }
  • @PostAuthorize

    • 사용자가 ADMIN 역할을 갖고 있는 경우에만 해당 메서드가 결과값을 반환합니다.

      @Component
      public class BankService {
      	@PostAuthorize("hasRole('ADMIN')")
      	public Account readAccount(Long id) {
              // ... is only invoked if the `Authentication` has the `ROLE_ADMIN` authority
      	}
      }
  • @PreFilter

    • accounts 에서 사용자의 이름과 일치한 Account 만 해당 메서드의 매개변수로서 수행됩니다.

      @Component
      public class BankService {
      	@PreFilter("filterObject.owner == authentication.name")
      	public Collection<Account> updateAccounts(Account... accounts) {
              // ... `accounts` will only contain the accounts owned by the logged-in user
              return updated;
      	}
      }
  • @PostFilter

    • accounts 는 사용자의 이름과 일치한 Account 만 포함되어 반환됩니다.

      @Component
      public class BankService {
      	@PostFilter("filterObject.owner == authentication.name")
      	public Collection<Account> readAccounts(String... ids) {
              // ... the return value will be filtered to only contain the accounts owned by the logged-in user
              return accounts;
      	}
      }

Method Security 작동 원리

@Service
public class MyCustomerService {
    @PreAuthorize("hasAuthority('permission:read')")
    @PostAuthorize("returnObject.owner == authentication.name")
    public Customer readCustomer(String id) { ... }
}
  • 위 코드에 대한 Method Security 의 작동 원리를 알아보겠습니다.

  1. Spring AOP 는 readCustomerproxy method를 호출합니다.
    proxyadvisor 들 중에서, @PreAuthorize 포인트컷과 일치하는 AuthorizationManagerBeforeMethodInterceptor 를 호출합니다.
  2. 이 인터셉터는 PreAuthorizeAuthorizationManager 의 check 함수를 호출합니다.
  3. Authorization Manager 는 @PreAuthorize 의 SpEL 표현식을 파싱하기 위해 MethodSecurityExpressionHandler 를 사용합니다.
    그리고, Supplier<Authentication>MethodInvocation 을 포함하며 애노테이션의 SpEL 표현식에 대응되는 EvaluationContextMethodSecurityExpressionRoot 로 부터 생성합니다.
  4. 이 인터셉터는 표현식을 평가하기 위해 context를 사용합니다. 특히, Supplier 로 부터 Authentication를 가져온 후, 권한을 확인합니다.
  5. 만약 권한 인증이 성공한다면, Spring AOP 는 메서드 호출을 계속 진행합니다.
  6. 권한 인증이 실패한다면, 인터셉터는 AuthorizationDeniedEvent 를 발행하고 AccessDeniedException 예외를 던집니다.
    해당 예외는 ExceptionTranslationFilterAccessDeniedException 을 처리하며, 403 상태 코드를 응답에 전달합니다.
  7. 메서드가 반환된 후, Spring AOP는 @PostAuthorize 포인트컷과 일치하는 AuthorizationManagerAfterMethodInterceptor 를 호출합니다.
    이후 진행과정은 위 과정과 유사하며, PostAuthorizeAuthorizationManager 를 사용한다는 차이점이 있습니다.
  8. 만약 권한 인증이 성공한다면, 일반적으로 계속 흐름이 진행됩니다.
  9. 권한 인증이 실패한다면, 인터셉터는 AuthorizationDeniedEvent 를 발행하고 AccessDeniedException 예외를 던집니다.
    해당 예외는 ExceptionTranslationFilterAccessDeniedException 을 처리하며, 403 상태 코드를 응답에 전달합니다.

Tip
만약 메서드가 HTTP 요청의 context 에 의해 호출되지 않았다면, AccessDeniedException 을 처리하기 위한 핸들러를 따로 작성해야 합니다.

profile
소통을 중요하게 여기며, 정보의 공유를 통해 완전한 학습을 이루어 냅니다.

0개의 댓글