🎯 목표 : 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
에서 설정한 메소드 체인 정보를 기반으로 생성된다.