[Section 4] 권한 부여 구성요소

Kim·2022년 11월 22일
0

Boot Camp

목록 보기
53/64
post-thumbnail
post-custom-banner

이전 챕터에서 보호된 애플리케이션 개발을 위해 반드시 알아야 하는 중요한 보안 요소 두 가지 중 하나인 인증(Authentication)에 대해 살펴봤다.
애플리케이션 서비스를 이용하기 위한 사용자 인증에 성공해도, 체크해야 할 또 하나의 보안 요소가 있다. 그것은 바로 권한 부여(Authorization, 인가)이다.
권한 부여란, 인증에 성공한 사용자여도 부여된 권한 범위 내에서 애플리케이션 리소스에 접근할 수 있어야함을 의미한다.


Spring Security의 컴포넌트로 보는 권한 부여 처리 흐름

Spring Security Filter Chain에서 URL을 통해 사용자의 액세스를 제한하는 권한 부여 Filter는 바로 AuthorizationFilter이다.
AuthorizationFilter는 (1)과 같이 SecurityContextHolder로 부터 Authentication을 획득한다.
그리고 (2)와 같이 SecurityContextHolder로 부터 획득한Authentication과 HttpServletRequest를 AuthorizationManager에게 전달한다.

AuthorizationManager는 권한 부여 처리를 총괄하는 매니저 역할을 하는 인터페이스이며, RequestMatcherDelegatingAuthorizationManagerAuthorizationManager를 구현하는 구현체 중 하나이다.

RequestMatcherDelegatingAuthorizationManager는 RequestMatcher 평가식을 기반으로 해당 평가식에 매치되는 AuthorizationManager에게 권한 부여 처리를 위임하는 역할을 한다.
RequestMatcherDelegatingAuthorizationManager 직접 권한 부여 처리를 하는 것이 아닌, RequestMatcher를 통해 매치되는 AuthorizationManager 구현 클래스에게 위임한다.

(3)에서는 RequestMatcherDelegatingAuthorizationManager 내부에서 매치되는 AuthorizationManager 구현 클래스가 있다면 해당 AuthorizationManager 구현 클래스가 사용자의 권한을 체크한다.

적절한 권한이라면, 다음 요청 프로세스를 계속 이어가고, 적절한 권한이 아니라면 AccessDeniedException이 throw 되고 ExceptionTranslationFilter가 AccessDeniedException을 처리한다.

Spring Security의 권한 부여 컴포넌트

💡 AuthorizationFilter

...

	// (1)
	public AuthorizationFilter(AuthorizationManager<HttpServletRequest> authorizationManager) {
		Assert.notNull(authorizationManager, "authorizationManager cannot be null");
		this.authorizationManager = authorizationManager;
	}
    
    @Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request); // (2)
		this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, decision);
		...
		filterChain.doFilter(request, response);
	}
    
...

}

AuthorizationFilter 객체가 생성될 때, (1)과 같이 AuthorizationManager를 DI 받는 것을 볼 수 있다.
DI 받은 AuthorizationManager를 통해 권한 부여 처리를 진행한다.

DI 받은 AuthorizationManager의 check() 메서드를 호출하여 적절한 권한 부여 여부를 체크한다.
check() 메서드는 AuthorizationManager 구현 클래스에 따라 권한 체크 로직이 다른데, URL 기반으로 권한 부여 처리를 하는 AuthorizationFilter는 AuthorizationManager의 구현 클래스로 RequestMatcherDelegatingAuthorizationManager를 사용한다.

💡 AuthorizationManager

@FunctionalInterface
public interface AuthorizationManager<T> {
  ...
  ...

	@Nullable
	AuthorizationDecision check(Supplier<Authentication> authentication, T object);

}

AuthorizationManager는 권한 부여 처리를 총괄하는 매니저 역할을 하는 인터페이스이다.
check() 메서드 하나만 정의되어 있고, Supplier와 제너릭 타입의 객체를 파라미터로 갖는다.

💡 RequestMatcherDelegatingAuthorizationManager

public final class RequestMatcherDelegatingAuthorizationManager implements AuthorizationManager<HttpServletRequest> {

  ...

	@Override
	public AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest request) {
		...

    	 // (1)
		for (RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>> mapping : this.mappings) {

			RequestMatcher matcher = mapping.getRequestMatcher(); // (2)
			MatchResult matchResult = matcher.matcher(request);
			if (matchResult.isMatch()) {   // (3)
				AuthorizationManager<RequestAuthorizationContext> manager = mapping.getEntry();
				if (this.logger.isTraceEnabled()) {
					this.logger.trace(LogMessage.format("Checking authorization on %s using %s", request, manager));
				}
				return manager.check(authentication,
						new RequestAuthorizationContext(request, matchResult.getVariables()));
			}
		}
		this.logger.trace("Abstaining since did not find matching RequestMatcher");
		return null;
	}
}

RequestMatcherDelegatingAuthorizationManager는 AuthorizationManager의 구현 클래스 중 하나이다.
직접 권한 부여 처리를 수행하지 않고, RequestMatcher를 통해 매치되는 AuthorizationManager 구현 클래스에게 권한 부여 처리를 위임한다.

check() 메서드 내부에서 (1)과 같이 루프를 돌면서 RequestMatcherEntry 정보를 얻은 후, (2)와 같이 RequestMatcher 객체를 얻는다.
(3)과 같이 MatchResult.isMatch()true이면 AuthorizationManager 객체를 얻은 뒤, 사용자 권한을 체크한다.

RequestMatcher는 SecurityConfiguration에서 .antMatchers("/orders/**").hasRole("ADMIN")와 같은 메서드 체인 정보를 기반으로 생성된다.

🔑Key Summary

  • AuthorizationFilter는 URL을 통해 사용자의 액세스를 제한하는 권한 부여 Filter이며, Spring Security 5.5 버전부터 FilterSecurityInterceptor를 대체한다.

  • AuthorizationManager는 이름 그대로 권한 부여 처리를 총괄하는 매니저 역할을 하는 인터페이스이다.

  • RequestMatcherDelegatingAuthorizationManager AuthorizationManager의 구현 클래스 중 하나이며, 직접 권한 부여 처리를 수행 하지 않고 RequestMatcher를 통해 매치되는 AuthorizationManager 구현 클래스에게 권한 부여 처리를 위임한다.
    RequestMatcher는 SecurityConfiguration에서 .antMatchers("/orders/**").hasRole("ADMIN")와 같은 메서드 체인 정보를 기반으로 생성된다.


접근 제어 표현식

Spring Security에서는 웹 및 메서드 보안을 위해 표현식(Spring EL)을 사용할 수 있다.

💡 Spring Security에서 지원하는 표현식

  • hasRole(Stirng role)
    현재 보안 주체가 지정된 역할을 갖고 있는지 여부를 확인하고, 지정된 역할을 갖고 있다면 true를 리턴한다.
    피라미터로 넘긴 role이 ROLE_로 시작하지 않으면 기본적으로 추가한다. ➡ hasRole(’admin’)

  • hasAnyRole(String… roles)
    현재 보안 주체가 지정한 역할 중 1개라도 갖고 있으면 true를 리턴한다.
    문자열 리스트를 ,로 구분해서 전달한다. ➡ hasAnyRole(’admin’, ‘user’)

    . . .

    📗 Reference - Spring EL 표현식

post-custom-banner

0개의 댓글