#spring #spring-boot #kotlin #spring-security #spring-webflux
Вопрос:
В приложении spring-boot 2.4 у меня есть два SecurityWebFilterChain
s. Только для одного из них я хочу добавить несколько WebFilter
s через addFilterBefore().
@Configuration
@EnableWebFluxSecurity
class WebSecurityConfig {
@Bean
fun filter1(service: Service): WebFilter = Filter1(service)
@Bean
fun filter2(component: Component): WebFilter = Filter2(component)
@Bean
@Order(1)
fun apiSecurityConfiguration(
http: ServerHttpSecurity,
filter1: WebFilter,
filter2: WebFilter
): SecurityWebFilterChain = http
.securityMatcher(pathMatchers("/path/**"))
.addFilterBefore(filter1, SecurityWebFiltersOrder.AUTHENTICATION)
.addFilterAt(filter2, SecurityWebFiltersOrder.AUTHENTICATION)
.build()
@Bean
@Order(2)
fun actuatorSecurityConfiguration(
http: ServerHttpSecurity,
reactiveAuthenticationManager: ReactiveAuthenticationManager
): SecurityWebFilterChain = http
.securityMatcher(pathMatchers("/manage/**"))
.authenticationManager(reactiveAuthenticationManager)
.httpBasic { }
.build()
}
Однако, поскольку эти WebFilter
s создаются как компоненты, они регистрируются автоматически и применяются ко всем запросам, по-видимому, вне цепочки безопасности.
Для фильтров сервлетов можно отключить эту регистрацию с помощью a FilterRegistrationBean
(см. Документацию по весенней загрузке).
Существует ли аналогичный способ для реактивных WebFilter
s, или мне нужно добавить дополнительную фильтрацию URL-адресов в эти фильтры?
Комментарии:
1. Почему бы просто не создать их в виде бобов?
2. Эти фильтры зависят от других компонентов, для краткости не показанных в примере.
3. Затем автоматически подключайте эти компоненты и загружайте их в конструктор фильтра во время создания экземпляров.
4. Звучит выполнимо, но также немного противоречит принципам инверсии контроля.
5. ну, вы уже игнорируете принципы IoC, вручную создав экземпляр и установив фильтр… ооооо…… вы начали это, а не я 🙂
Ответ №1:
Чтобы найти решение, мы сначала должны немного глубже разобраться в том, как работает spring и его внутренние компоненты.
Все компоненты типа WebFilter
автоматически добавляются в основную цепочку фильтров веб-обработки.
См. Документацию по весенней загрузке по этой теме:
Компоненты веб-фильтра, найденные в контексте приложения, будут автоматически использоваться для фильтрации каждого обмена.
Так что это происходит, даже если вы хотите, чтобы они применялись только к определенной цепочке фильтров защиты пружин.
(ИМХО, это немного недостаток spring-безопасности, чтобы повторно использовать интерфейсы Filter
or WebFilter
и не иметь чего-то специфичного для безопасности с той же подписью.)
В коде соответствующая часть находится в сборщике WebHttpHandlerBuilder spring-web
public static WebHttpHandlerBuilder applicationContext(ApplicationContext context) {
// ...
List<WebFilter> webFilters = context
.getBeanProvider(WebFilter.class)
.orderedStream()
.collect(Collectors.toList());
builder.filters(filters -> filters.addAll(webFilters));
// ...
}
Который, в свою очередь, вызывается в конфигурации HttpHandlerAutoConfiguration spring-boot для создания основного HttpHandler.
@Bean
public HttpHandler httpHandler(ObjectProvider<WebFluxProperties> propsProvider) {
HttpHandler httpHandler = WebHttpHandlerBuilder.applicationContext(this.applicationContext).build();
// ...
return httpHandler;
}
Чтобы эти фильтры не применялись ко всем биржам, возможно, можно просто не создавать их как компоненты, а создавать вручную, как предложено в комментарии выше. Тогда поставщик BeanProvider не найдет их и не добавит в HttpHandler. Однако вы покидаете страну IoC и теряете автоконфигурацию фильтров. Не идеально, когда эти фильтры становятся более сложными или когда их у вас много.
Поэтому вместо этого мое решение состоит в том, чтобы вручную настроить a HttpHandler
для моего приложения, которое не добавляет мои фильтры, относящиеся к безопасности, в глобальную цепочку фильтров.
Чтобы это сработало, я сначала объявляю интерфейс маркера для своих фильтров.
interface NonGlobalFilter
class MySecurityFilter : WebFilter, NonGlobalFilter {
// ...
}
Затем требуется класс конфигурации, в котором создается пользовательский HttpHandler. Удобно, что WebHttpHandlerBuilder имеет метод для управления списком фильтров с потребителем.
Это помешает spring-boot использовать свой собственный HttpHandler из конфигурации HttpHandlerAutoConfiguration, поскольку он снабжен @ConditionalOnMissingBean(HttpHandler.class)
аннотациями .
@Configuration
class WebHttpHandlerConfiguration(
private val applicationContext: ApplicationContext
) {
@Bean
fun httpHandler() = WebHttpHandlerBuilder
.applicationContext(applicationContext)
.filters {
it.removeIf {
webFilter -> webFilter is NonGlobalFilter
}
}
.build()
}
И это все! Как всегда, spring предоставляет множество полезных настроек по умолчанию из коробки, но когда это встанет у вас на пути, будет возможность настроить его по мере необходимости.