Spring Boot: 3.1.5
Java: 17
Spring Boot Security: 6.1.5
SecurityConfig 설정파일
@Configuration
@EnableWebSecurity
public class SecurityConfig {
...
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web
.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.requestMatchers("/mypage").hasRole("USER")
.anyRequest().authenticated()
);
http
.formLogin((form) -> form
.loginPage("/login")
.defaultSuccessUrl("/")
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.permitAll()
);
return http.build();
}
...
}
}
AuthorizationFilter는 URL 기반 인가 처리를 담당하는 필터이다.

AuthorizationFilter의 doFilter() 메서드 호출

AuthorizationManager로 RequestMatcherDelegatingAuthorizationManager 클래스를 사용한다. this.authorizationManager에 RequestMatcherDelegatingAuthorizationManager를 주입받은 것을 아래 사진에서 확인할 수 있다.
SecurityContextHolderStrategy로 부터 인증 정보 얻는다. RequestMatcherDelegatingAuthorizationManager의 check() 메서드 호출
RequestMatcherDelegatingAuthorizationManager는 AuthorizationManager의 구현체로 RequestMatcher 평가식을 기반으로 평가식에 매치되는 AuthorizationManager 구현클래스에게 권한 심사를 위임한다.

RequestMatcherDelegatingAuthorizationManager 클래스의 mappings 필드는 RequestMatcherEntry 객체로 구성된 리스트이다. RequestMatcherEntry 객체는 requestMatcher에 경로를 entry에 AuthenticaionManager 구현체를 담고있다.

3-1. matcher() 메서드를 통해 mappings의 requestMatcher와 파라미터로 넘어온 request의 URI가 매칭되는지 확인한다. 매칭되는 경우 while문을 빠져나간다.
3-2. mapping.getEntry() 로 인가처리를 위임할 AuthorizationManager 구현클래스를 꺼낸다.
3-3. 찾은 AuthorizationManager 구현체에게 인가처리를 위임한다.
찾은 AuthorizationManager 구현체의 check() 메서드가 호출되고 사용자의 권한을 심사한다.
RequestMatcherDelegatingAuthorizationManager 에서 반환받은 AuthorizationDecision을 AuthorizationFilter로 반환한다.
AuthorizationFilter가 AuthorizationDecision을 반환받았다.

AuthorizationDecision[granted=true]을 반환받으면 다음 필터를 호출한다.AuthorizationDecision[granted=false] 을 반환받으면 AccessDeniedException 을 던지고 ExceptionTranslationFilter가 AccessDeniedException()을 처리한다.RequestMatcherDelegatingAuthorizationManager의 mappings 필드를 보면 /mypage 요청을 처리할 AuthorizationManager 구현체는 AuthorityAuthorizationManager이다.
AuthorityAuthorizationManager 는 /mypage의 권한정보인 ROLE_USER를 가지고 있다. AuthoritiesAuthorizationManager의 check() 메서드를 호출하면서 인증정보와 권한정보를 넘긴다.

/mypage에 접근하려면 ROLE_USER권한을 가져야 하므로 AuthorityAuthorizationDecision[granted=false] 을 반환한다.

decision.isGranted()=false이므로 AccessDeniedException 예외를 던진다.
초기화 될 때 스프링 시큐리티가 SecurityConfig 파일 읽어 RequestMatcher와 AuthorizaionManager 구현체를 생성해 RequestMatcherDelegatingAuthorizationManager의 mappings 필드 초기화한다.
SecurityConfig파일에서 USER를 읽어와AuthorityAuthorizationManager 객체 생성하면서 USER 권한정보를 넘긴다.
managerBuilder에 생성한 MvcRequestMatcher[pattern='/mypage']와 AuthorityAuthorizationManager[authorities=[ROLE_USER]] add한다.
RequestMatcherDelegatingAuthorizationManager의 mappings 필드에 managerBuilder에 저장된 값들이 전달된다.로그인하지 않고 /mypage를 호출했을 경우와 1,2,3번까지는 동일하다.
ROLE_USER권한을 가진 사용자가 로그인하고 /mypage를 호출했을 경우를 알아보자.
/mypage에는 ROLE_USER권한을 가진 사용자만 접근할 수 있는데 현재 사용자는 ROLE_USER권한을 가진다. 따라서 AuthorityAuthorizationDecision[granted=true] 을 반환한다.

decision.isGranted()=true이므로 정상적으로 다음 필터를 호출한다.