스프링 / springboot / 스프링부트 / security
Spring 의 securityWebFilterChain 에서 filter 가 추가되는 과정
WebFilterChainProxy.filter()
에서WebFluxSecurityConfiguration.securityWebFilterChains
을 돌면서 실행한다.WebFilterChainProxy
은@Bean
WebFluxSecurityConfiguration.springSecurityWebFilterChainFilter
에 의해서 new 된다.securityWebFilterChains
은@Autowire
setSecurityWebFilterChains(List<SecurityWebFilterChain> securityWebFilterChains)
에 의해 new 된다.WebFluxSecurityConfiguration
은@EnableWebFluxSecurity
에 의해 불려진다.@EnableWebFluxSecurity
은SecurityConfig
에 설정돼 있다.@Bean
SecurityConfig.securityWebFilterChain
에서 filter를 정하고,ServerHttpSecurity.build()
를 호출하는데, 이 때 filter들이 실제로 추가된다.- formLogin 인 경우 login url 인 ‘/login’ 도 설정된다.
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig
@Bean
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
...
.authorizeExchange(exchange -> exchange
...
.and()
.formLogin(formLogin -> formLogin
.authenticationSuccessHandler(authenticationSuccessHandler)
.authenticationFailureHandler(authenticationFailureHandler)
)
);
return http.build(); // ServerHttpSecurity.build
}
public class ServerHttpSecurity
private FormLoginSpec formLogin;
public SecurityWebFilterChain build() {
if (this.formLogin != null) {
if (this.formLogin.authenticationManager == null) {
this.formLogin.authenticationManager(this.authenticationManager);
}
if (this.formLogin.securityContextRepository != null) {
this.formLogin.securityContextRepository(this.formLogin.securityContextRepository);
}
else if (this.securityContextRepository != null) {
this.formLogin.securityContextRepository(this.securityContextRepository);
}
else {
this.formLogin.securityContextRepository(new WebSessionServerSecurityContextRepository());
}
// add filter to the this.webFilters
this.formLogin.configure(this);
}
...
AnnotationAwareOrderComparator.sort(this.webFilters);
List<WebFilter> sortedWebFilters = new ArrayList<>();
this.webFilters.forEach((f) -> {
if (f instanceof OrderedWebFilter) {
f = ((OrderedWebFilter) f).webFilter;
}
sortedWebFilters.add(f);
});
sortedWebFilters.add(0, new ServerWebExchangeReactorContextWebFilter());
return new MatcherSecurityWebFilterChain(getSecurityMatcher(), sortedWebFilters);
}
public final class FormLoginSpec {
protected void configure(ServerHttpSecurity http) {
if (this.authenticationEntryPoint == null) {
this.isEntryPointExplicit = false;
loginPage("/login");
}
...
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({ ServerHttpSecurityConfiguration.class, WebFluxSecurityConfiguration.class,
ReactiveOAuth2ClientImportSelector.class })
@Configuration
public @interface EnableWebFluxSecurity {
}
@Configuration(proxyBeanMethods = false)
class WebFluxSecurityConfiguration {
private List<SecurityWebFilterChain> securityWebFilterChains;
...
@Autowired(required = false)
void setSecurityWebFilterChains(List<SecurityWebFilterChain> securityWebFilterChains) {
// SecurityConfig.securityWebFilterChain()
this.securityWebFilterChains = securityWebFilterChains;
}
@Bean(SPRING_SECURITY_WEBFILTERCHAINFILTER_BEAN_NAME)
@Order(WEB_FILTER_CHAIN_FILTER_ORDER)
WebFilterChainProxy springSecurityWebFilterChainFilter() {
return new WebFilterChainProxy(getSecurityWebFilterChains());
}
private List<SecurityWebFilterChain> getSecurityWebFilterChains() {
List<SecurityWebFilterChain> result = this.securityWebFilterChains;
if (ObjectUtils.isEmpty(result)) {
return Arrays.asList(springSecurityFilterChain());
}
return result;
}
public class WebFilterChainProxy implements WebFilter {
private final List<SecurityWebFilterChain> filters;
public WebFilterChainProxy(List<SecurityWebFilterChain> filters) {
this.filters = filters; // WebFluxSecurityConfiguration.securityWebFilterChains
}
...
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return Flux.fromIterable(this.filters)
.filterWhen((securityWebFilterChain) -> securityWebFilterChain.matches(exchange)).next()
.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))
.flatMap((securityWebFilterChain) -> securityWebFilterChain.getWebFilters().collectList())
.map((filters) -> new FilteringWebHandler(chain::filter, filters)).map(DefaultWebFilterChain::new)
.flatMap((securedChain) -> securedChain.filter(exchange)); // securedChain is `DefaultWebFilterChain`
}
...
}
@AutoConfiguration(after = { WebFluxAutoConfiguration.class })
@ConditionalOnClass({ DispatcherHandler.class, HttpHandler.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnMissingBean(HttpHandler.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
public class HttpHandlerAutoConfiguration {
@Configuration(proxyBeanMethods = false)
public static class AnnotationConfig {
...
@Bean
public HttpHandler httpHandler(ObjectProvider<WebFluxProperties> propsProvider) {
HttpHandler httpHandler = WebHttpHandlerBuilder.applicationContext(this.applicationContext).build();
...
return httpHandler;
}
}
}
public final class WebHttpHandlerBuilder{
public static WebHttpHandlerBuilder applicationContext(ApplicationContext context) {
...
// set this.filters
List<WebFilter> webFilters = context
.getBeanProvider(WebFilter.class)
.orderedStream()
.collect(Collectors.toList());
builder.filters(filters -> filters.addAll(webFilters));
}
public HttpHandler build() {
WebHandler decorated = new FilteringWebHandler(this.webHandler, this.filters);
decorated = new ExceptionHandlingWebHandler(decorated, this.exceptionHandlers);
HttpWebHandlerAdapter adapted = new HttpWebHandlerAdapter(decorated);
...
// return adapted
return (this.httpHandlerDecorator != null ? this.httpHandlerDecorator.apply(adapted) : adapted);
}
}
public class FilteringWebHandler extends WebHandlerDecorator
public FilteringWebHandler(WebHandler handler, List<WebFilter> filters) {
super(handler);
this.chain = new DefaultWebFilterChain(handler, filters);
}
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
return this.chain.filter(exchange);
}
public class DefaultWebFilterChain implements WebFilterChain
public DefaultWebFilterChain(WebHandler handler, List<WebFilter> filters) {
Assert.notNull(handler, "WebHandler is required");
this.allFilters = Collections.unmodifiableList(filters);
this.handler = handler;
DefaultWebFilterChain chain = initChain(filters, handler);
this.currentFilter = chain.currentFilter;
this.chain = chain.chain;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange) {
return Mono.defer(() ->
this.currentFilter != null && this.chain != null ?
invokeFilter(this.currentFilter, this.chain, exchange) :
this.handler.handle(exchange));
}
private Mono<Void> invokeFilter(WebFilter current, DefaultWebFilterChain chain, ServerWebExchange exchange) {
String currentName = current.getClass().getName();
return current.filter(exchange, chain).checkpoint(currentName + " [DefaultWebFilterChain]");
}
requst 를 처리시점에 호출되는 filter()
HttpWebHandlerAdapter
에서getDelegate().handle(exchange)
ExceptionHandlingWebHandler.handle()
에서super.handle()
을 호출FilteringWebHandler.handle()
을 호출하게 된다.FilteringWebHandler.handle()
에서DefaultWebFilterChain.filter(exchange)
를 호출
public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHandler
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
...
ServerWebExchange exchange = createExchange(request, response);
...
return getDelegate().handle(exchange)
.doOnSuccess(aVoid -> logResponse(exchange))
.onErrorResume(ex -> handleUnresolvedError(exchange, ex))
.then(Mono.defer(response::setComplete));
}
public class ExceptionHandlingWebHandler extends WebHandlerDecorator
...
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
Mono<Void> completion;
try {
// WebHandlerDecorator.handle --> FilteringWebHandler.handle
completion = super.handle(exchange);
}
catch (Throwable ex) {
...
}
for (WebExceptionHandler handler : this.exceptionHandlers) {
completion = completion.onErrorResume(ex -> handler.handle(exchange, ex));
}
return completion;
}
public class WebHandlerDecorator implements WebHandler
private final WebHandler delegate;
public WebHandlerDecorator(WebHandler delegate) {
...
this.delegate = delegate;
}
...
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
return this.delegate.handle(exchange);
}
/login 호출
curl http://localhost:8888/login
DefaultWebFilterChain.filter
- –>
AuthenticationWebFilter.filter
- -->
AuthenticationWebFilter.authenticate()
- -->
authenticationManager.authenticate(token)
- -->
AbstractUserDetailsReactiveAuthenticationManager.authenticate
댓글 없음:
댓글 쓰기