РЕДАКТИРОВАТЬ: вот окончательное решение: вот как я это сделал:
Цель: удалить WebFilter Spring Security из HttpHandler по умолчанию и вставить его между RoutePredicateRouteMapping и FilteringWebHandler из Spring Cloud Gateway.
Зачем: потому что мне нужно знать идентификатор приложения при выполнении индивидуального процесса аутентификации. Этот идентификатор приложения прикрепляется к запросу с помощью RoutePredicateRouteMapping путем сопоставления URL-адреса запроса с предопределенным списком.
Как я это сделал: 1- Удаление WebFilter Spring Security Я создал bean-компонент HttpHandler, который вызывает WebHttpHandlerBuilder по умолчанию, а затем настраивает фильтры. В качестве бонуса я удалил ненужные фильтры, чтобы повысить производительность моего шлюза API.
@Bean
public HttpHandler httpHandler() {
WebHttpHandlerBuilder webHttpHandlerBuilder = WebHttpHandlerBuilder.applicationContext(this.applicationContext);
MyAuthenticationHandlerAdapter myAuthenticationHandlerAdapter = this.applicationContext.getBean(MY_AUTHENTICATED_HANDLER_BEAN_NAME, MyAuthenticationHandlerAdapter.class);
webHttpHandlerBuilder
.filters(filters ->
myAuthenticationHandlerAdapter.setSecurityFilter(
Collections.singletonList(filters.stream().filter(f -> f instanceof WebFilterChainProxy).map(f -> (WebFilterChainProxy) f).findFirst().orElse(null))
)
);
return webHttpHandlerBuilder.filters(filters -> filters
.removeIf(f -> f instanceof WebFilterChainProxy || f instanceof WeightCalculatorWebFilter || f instanceof OrderedHiddenHttpMethodFilter))
.build();
}
2- Обертывание FilteringWebHandler Spring Cloud Gateway с помощью Spring Web FilteringWebHandler с добавленным WebFilter Я создал свой собственный HandlerAdapter, который соответствовал бы FilteringWebHandler Spring Cloud Gateway, и обернул его с помощью Spring Web FilteringWebHandler плюс фильтр безопасности, который я извлек на первом этапе.
@Bean
public MyAuthenticationHandlerAdapter myAuthenticationHandlerAdapter() {
return new MyAuthenticationHandlerAdapter();
}
public class MyAuthenticationHandlerAdapter implements HandlerAdapter {
@Setter
private List<WebFilter> securityFilter = new ArrayList<>();
@Override
public boolean supports(Object handler) {
return handler instanceof FilteringWebHandler;
}
@Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
org.springframework.web.server.handler.FilteringWebHandler filteringWebHandler = new org.springframework.web.server.handler.FilteringWebHandler((WebHandler) handler, securityFilter);
Mono<Void> mono = filteringWebHandler.handle(exchange);
return mono.then(Mono.empty());
}
}
Таким образом, я мог добиться лучшей производительности с помощью настраиваемого конвейера HttpHandler, который, как я полагаю, будет ориентирован на будущее.
КОНЕЦ РЕДАКТИРОВАНИЯ
Spring Security для WebFlux реализован как WebFilter, который запускается почти сразу после получения запроса. Я реализовал собственный конвертер аутентификации и менеджер аутентификации, который извлекает некоторые переменные из заголовка и URL-адреса и использует их для аутентификации. Это работает без проблем.
Теперь мне нужно было добавить еще одну переменную, взятую из RoutePredicateRouteMapping, прежде чем аутентификация будет выполнена. Я хочу именно удалить WebFilter (называемый WebFilterChainProxy) из его текущей позиции и поместить его между RoutePredicateRouteMapping и FilteringWeHandler.
Вот как проходит процесс по умолчанию:
ChannelOperations вызывает ReactorHttpHandlerAdapter, который вызывает HttpWebHandlerAdapter, ExceptionHandlingWebHandler, а затем org.springframework.web.server.handler.FilterWebHandler.
Этот WebHandler вызовет свои фильтры, а затем вызовет DispatchHandler. Одним из таких фильтров является WebFilterChainProxy, который выполняет аутентификацию для Spring Security. Итак, первым делом нужно удалить отсюда фильтр.
Теперь DispatchHandler, который вызывается после фильтров, будет вызывать RoutePredicateHandlerMapping, который будет анализировать маршруты и дать мне идентификатор маршрута, который мне нужен, а затем вызовет org.springframework.cloud.gateway.handler.FilteringHandler (это не тот же FilteringHandler выше), и это, в свою очередь, вызовет другие фильтры Spring Cloud Gateway. Я хочу вызвать фильтр после RoutePredicatehandlerMapping и до org.springframework.cloud.gateway.handler.FilteringHandler. В конце концов я сделал следующее:
Я создал и WebHttpHandlerBuilder, который удалит WebFilterChainProxy и передаст его в качестве параметра настраиваемому DispatcherHandler. Теперь, когда фильтр удален, запрос пройдет первые уровни без необходимости аутентификации. В моем настроенном DispatcherHandler я бы вызвал RoutePredicateHandlerMapping, а затем передал бы переменную обмена в WebFilterChainProxy, чтобы выполнить аутентификацию, прежде чем передавать ее в org.springframework.cloud.gateway.handler.FilteringHandler, который работал отлично! Я все еще думаю, что переборщил с разработкой, и надеюсь, что есть способ сделать это, используя аннотации и компоненты конфигурации вместо всех этих настраиваемых классов (WebHttpHandlerBuilder и DispatcherHandler).
person
Sam
schedule
05.10.2018