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

📌 권한 부여 처리 흐름
- 사용자가 로그인 인증에 성공한 후 인증된 사용자에게 부여하는 권한 처리 흐름을 정리해 보았다.
AuthorizationFilter는 Spring Security Filter Chain에서 URL을 통해 사용자의 리소스에 대한 액세스를 제한하는 권한 부여 필터다.
AuthorizationFilter는 SecurityContextHolder로 부터 Authentication을 전달 받는다.
Authentication와 HttpServletRequest를 AuthorizationManager에게 전달한다.
RequestMatcherDelegatingAuthorizationManager는 AuthorizationManager의 구현체중 하나다.
RequestMatcherDelegatingAuthorizationManager는 직접 권한 부여 처리를 하는 것이 아니라 RequestMatcher를 통해 매치되는 AuthorizationManager의 구현체에게 권한 부여 처리를 위임해 주는 역할을 한다.
- 매치되는 구현체가 있다면, 해당 구현체는 사용자의 권한을 체크한다.
- 적절한 권한이라면 다음 요청 프로세스를 계속 이어 나간다.
- 적절한 권한이 아니라면
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() 메소드에서 AuthorizationManager의 check()를 호출하여 권한 부여 여부를 체크한다.
- URL 기반 권한 부여 처리를 하는
AuthorizationFilter는 RequestMatcherDelegatingAuthorizationManager를 사용한다.
📌 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 객체를 얻어 사용자 권한을 체크한다.
RequestMatcher는 SecurityConfiguration에서 설정한 메소드 체인 정보를 기반으로 생성된다.