회원가입을 한다고 해서 사이트에서 모든 권한을 가입자에게 쥐어주지는 않는다.
운영자와 회원이 접근할 수 있는 사이트의 영역은 분리되어 있고 회원도 다양한 계급으로 접근할 수 있는 영역이 분리되어 있는 경우도 존재하는데 SpringSecurity에서는 해당 User가 지금 접근하려는 영역에 접근할 수 있는지 권한을 확인하고 접근할 수 있는 권한을 부여하는데 인증처리에 이어 어떻게 접근권한을 부여하는지 살펴보려고 한다.
흐름은 다음과 같다
출처:https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html
AuthorizationFilter에서 스프링 인증처리흐름에서 abstractAuthenticationProcessingFilter에서 저장한 SecurityContextHolder에서 authentication을 가져오고 HttpServletRequest를 통해 check() 메서드를 진행한다
public class AuthorizationFilter extends OncePerRequestFilter {
...
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request);
//authorizationManager의 check메서드를통해 authentication의 권한을 확인한다.
this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, decision);
if (decision != null && !decision.isGranted()) {
throw new AccessDeniedException("Access Denied");
}
filterChain.doFilter(request, response);
}
private Authentication getAuthentication() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//인증처리흐름에서 abstractAuthenticationProcessingFilter에서 저장했던 SecurityContextHolder에서
//Authentication을 가져온다.
if (authentication == null) {
throw new AuthenticationCredentialsNotFoundException(
"An Authentication object was not found in the SecurityContext");
}
return authentication;
}
...
}
check() 메서드는 AuthorizationManager에서 선언되어져 있다.
@FunctionalInterface
public interface AuthorizationManager<T> {
...
@Nullable
AuthorizationDecision check(Supplier<Authentication> authentication, T object);
}
이중 해당 메서드를 HttpRequest와 Authentication을 파라미터로 받는 구현클래스는 RequestMatcherDelegatingAuthorizationManager이고, 해당 메서드에서 AuthorizationManager를 순회하면서 적합한 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) {
//for문을 통해 적합한 AuthorizationManager를 탐색한다
RequestMatcher matcher = mapping.getRequestMatcher();
MatchResult matchResult = matcher.matcher(request);
if (matchResult.isMatch()) {
AuthorizationManager<RequestAuthorizationContext> manager = mapping.getEntry();
//matchResult가 일치할 경우 AuthorizationManager 객체를 얻어서 사용자의 권한을 확인한다.
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;
}
...
}