AuthorizationFilter는 SecutityFilterChain에 속해 있는 필터 중 하나로, HttpServletRequest에게 인가를 제공한다.
AuthorizationFilter 필터 등록은 다음과 같이 SecurityFilterChain 등록시, authorizeHttpRequests()
메소드를 통해 등록하면 된다.
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authHttp ->
authHttp.requestMatchers(POST, "/join", "/login").permitAll())
...
return http.build();
}
먼저 AuthorizationFilter는 SecurityContextHolder에서 Authentication 객체를 얻어서 Authentication과 HttpServletRequest를 AuthorizationManager의 check()
메서드로 넘긴다.
AuthorizationManager의 check()
메서드는 AuthorizationDecision을 반환한다.
AuthorizationFilter는 반환받은 AuthorizationDecision을 검증하며 적절한 권한인지 확인하고, 검증에 실패하면 AccessDeniedException 예외를 발생시킨다.
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");
}
chain.doFilter(request, response);
만약 검증에 성공하면, 적절한 권한인 것이므로 다음 FilterChain을 이어간다.
AuthorizationManager는 인터페이스로, 인가(Authorization)를 담당한다.
@FunctionalInterface
public interface AuthorizationManager<T> {
default void verify(Supplier<Authentication> authentication, T object) {
AuthorizationDecision decision = check(authentication, object);
if (decision != null && !decision.isGranted()) {
throw new AccessDeniedException("Access Denied");
}
}
@Nullable
AuthorizationDecision check(Supplier<Authentication> authentication, T object);
}
AuthorizationManager 인터페이스에는 두 가지 메서드(verify()
, check()
)가 있다. verify 메서드는 내부적으로 check 메서드를 호출하고, check 메서드는 AuthorizationDecision을 반환한다.
public class AuthorizationDecision {
private final boolean granted;
public AuthorizationDecision(boolean granted) {
this.granted = granted;
}
public boolean isGranted() {
return this.granted;
}
@Override
public String toString() {
return getClass().getSimpleName() + " [granted=" + this.granted + "]";
}
}
AuthorizationDecision은 생성자로 boolean granted
를 넣어서 생성하고, isGranted()
메서드를 통해 인증 여부를 확인할 수 있다.
즉 AuthorizationManager의 check()
메서드를 통해 반환된 AuthorizationDecision을 verify()
메서드에서 검증하여 검증에 실패하면 AccessDeniedException 예외를 발생시킨다.
Autorization에는 많은 종류가 있다. 따라서 이를 가운데에서 중재하고 알맞은 AuthorizationManager로 연결시켜주는 것이 RequestMatcherDelegatingAuthorizationManager이다. RequestMatcherDelegatingAuthorizationManager는 AuthorizationManager의 구현체이다.
RequestMatcherDelegatingAuthorizationManager는 request에 match되는 AuthorizationManager를 받고, 그 Manager의 check 메서드를 실행시켜 그 반환값(AuthorizationDecision)을 반환한다.
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest 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();
return manager.check(authentication, new RequestAuthorizationContext(request, matchResult.getVariables()));
}
}
return DENY;
}
Reference
https://velog.io/@on5949/SpringSecurity-Authorization-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90
https://tech-monster.tistory.com/217?category=1075581
https://jaeyoungb.tistory.com/214