[Spring] Spring Security Authorization

Gogh·2023년 1월 2일
0

Spring

목록 보기
20/23

🎯 목표 : Spring Security 권한 부여 Flow 와 Component 에 대한 이해

📒 Spring Security Authorization Flow

image

📌 권한 부여 처리 흐름

  • 사용자가 로그인 인증에 성공한 후 인증된 사용자에게 부여하는 권한 처리 흐름을 정리해 보았다.
    1. AuthorizationFilterSpring Security Filter Chain에서 URL을 통해 사용자의 리소스에 대한 액세스를 제한하는 권한 부여 필터다.
      • AuthorizationFilterSecurityContextHolder로 부터 Authentication을 전달 받는다.
    2. AuthenticationHttpServletRequestAuthorizationManager에게 전달한다.
      • RequestMatcherDelegatingAuthorizationManagerAuthorizationManager의 구현체중 하나다.
      • RequestMatcherDelegatingAuthorizationManager는 직접 권한 부여 처리를 하는 것이 아니라 RequestMatcher를 통해 매치되는 AuthorizationManager의 구현체에게 권한 부여 처리를 위임해 주는 역할을 한다.
    3. 매치되는 구현체가 있다면, 해당 구현체는 사용자의 권한을 체크한다.
    4. 적절한 권한이라면 다음 요청 프로세스를 계속 이어 나간다.
    5. 적절한 권한이 아니라면 AccessDeniedException을 던져 ExceptionTranslationFilter가 예외를 처리하게 된다.

📒 Spring Security Authorization Component

📌 AuthorizationFilter

  • URL을 통해 사용자의 리소스 접근에 대한 액세스를 제한하는 권한 부여 필터다.
public class AuthorizationFilter extends OncePerRequestFilter {

	private final AuthorizationManager<HttpServletRequest> authorizationManager;

  //...
  //...

	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);
		this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, decision);
		if (decision != null && !decision.isGranted()) {
			throw new AccessDeniedException("Access Denied");
		}
		filterChain.doFilter(request, response);
	}

  //...
  //...

}
  • 생성자를 보면, AuthorizationManager 의존성 주입을 받는 것을 알수 있다. 주입받은 객체를 통해 권한 부여 처리를 진행한다.
  • doFilterInternal() 메소드에서 AuthorizationManagercheck()를 호출하여 권한 부여 여부를 체크한다.
  • URL 기반 권한 부여 처리를 하는 AuthorizationFilterRequestMatcherDelegatingAuthorizationManager를 사용한다.

📌 AuthorizationManager

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

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

}
  • AuthorizationManager 인터페이스는 check() 메소드 하나만 존재하며, Supplier와 제네릭 타입의 객체를 파라미터로 가진다.

📌 RequestMatcherDelegatingAuthorizationManager

  • AuthorizationManager의 구현체중 하나다 직접 권한 부여처리를 하지 않고 RequestMatcher를 통해 매치되는 AuthorizationManager 구현 클래에 권한 부여 처리를 위임해주는 역할을 한다.
public final class RequestMatcherDelegatingAuthorizationManager implements AuthorizationManager<HttpServletRequest> {

  //...
  //...

	@Override
	public AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest request) {
		if (this.logger.isTraceEnabled()) {
			this.logger.trace(LogMessage.format("Authorizing %s", request));
		}

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

			RequestMatcher matcher = mapping.getRequestMatcher();
			MatchResult matchResult = matcher.matcher(request);
			if (matchResult.isMatch()) {
				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;
	}
}
  • check()메소드 내부에서 for 루프를 돌며 RequestMatcherEntry 정보를 얻는다.
  • RequestMatcher 객체를 얻어 MatchResult와 매치 된다면 AuthorizationManager 객체를 얻어 사용자 권한을 체크한다.
  • RequestMatcherSecurityConfiguration에서 설정한 메소드 체인 정보를 기반으로 생성된다.
profile
컴퓨터가 할일은 컴퓨터가

0개의 댓글