mirror of https://github.com/halo-dev/halo
Refactor AdditionalWebFilter load (#5349)
#### What type of PR is this? /kind improvement /area core /milestone 2.13.x #### What this PR does / why we need it: This PR creates AdditionalWebFilterChainProxy to call all additional filters instead of using SecurityWebFilterChain. Please note that: - the AdditionalWebFilterChainProxy should be executed before `org.springframework.security.web.server.WebFilterChainProxy`. - I don't change `UsernamePasswordAuthenticator` because of <https://github.com/halo-dev/halo/pull/5348>. The authenticator should be in Security scope instead of a standalone webfilter. See https://github.com/halo-dev/halo/issues/5300#issuecomment-1933436652 for more. #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/5300 #### Does this PR introduce a user-facing change? ```release-note None ```pull/5362/head
parent
caf3e66e8c
commit
9178ad0e22
|
@ -15,6 +15,7 @@ import org.springframework.boot.autoconfigure.web.WebProperties;
|
|||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
|
@ -40,6 +41,8 @@ import run.halo.app.console.WebSocketRequestPredicate;
|
|||
import run.halo.app.core.extension.endpoint.CustomEndpoint;
|
||||
import run.halo.app.core.extension.endpoint.CustomEndpointsBuilder;
|
||||
import run.halo.app.infra.properties.HaloProperties;
|
||||
import run.halo.app.plugin.extensionpoint.ExtensionGetter;
|
||||
import run.halo.app.webfilter.AdditionalWebFilterChainProxy;
|
||||
|
||||
@Configuration
|
||||
public class WebFluxConfig implements WebFluxConfigurer {
|
||||
|
@ -200,4 +203,23 @@ public class WebFluxConfig implements WebFluxConfigurer {
|
|||
ProxyFilter ucProxyFilter() {
|
||||
return new ProxyFilter("/uc/**", haloProp.getUc().getProxy());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a WebFilterChainProxy for all AdditionalWebFilters.
|
||||
*
|
||||
* <p>The reason why the order is -101 is that the current
|
||||
* AdditionalWebFilterChainProxy should be executed before WebFilterChainProxy
|
||||
* and the order of WebFilterChainProxy is -100.
|
||||
*
|
||||
* <p>See {@code org.springframework.security.config.annotation.web.reactive
|
||||
* .WebFluxSecurityConfiguration#WEB_FILTER_CHAIN_FILTER_ORDER} for more
|
||||
*
|
||||
* @param extensionGetter extension getter.
|
||||
* @return additional web filter chain proxy.
|
||||
*/
|
||||
@Bean
|
||||
@Order(-101)
|
||||
AdditionalWebFilterChainProxy additionalWebFilterChainProxy(ExtensionGetter extensionGetter) {
|
||||
return new AdditionalWebFilterChainProxy(extensionGetter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,9 +30,7 @@ import run.halo.app.core.extension.service.UserService;
|
|||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
import run.halo.app.infra.AnonymousUserConst;
|
||||
import run.halo.app.infra.properties.HaloProperties;
|
||||
import run.halo.app.plugin.extensionpoint.ExtensionGetter;
|
||||
import run.halo.app.security.DefaultUserDetailService;
|
||||
import run.halo.app.security.DynamicMatcherSecurityWebFilterChain;
|
||||
import run.halo.app.security.authentication.SecurityConfigurer;
|
||||
import run.halo.app.security.authentication.login.CryptoService;
|
||||
import run.halo.app.security.authentication.login.PublicKeyRouteBuilder;
|
||||
|
@ -60,7 +58,6 @@ public class WebServerSecurityConfig {
|
|||
RoleService roleService,
|
||||
ObjectProvider<SecurityConfigurer> securityConfigurers,
|
||||
ServerSecurityContextRepository securityContextRepository,
|
||||
ExtensionGetter extensionGetter,
|
||||
ReactiveExtensionClient client,
|
||||
PatJwkSupplier patJwkSupplier) {
|
||||
|
||||
|
@ -92,7 +89,7 @@ public class WebServerSecurityConfig {
|
|||
// Integrate with other configurers separately
|
||||
securityConfigurers.orderedStream()
|
||||
.forEach(securityConfigurer -> securityConfigurer.configure(http));
|
||||
return new DynamicMatcherSecurityWebFilterChain(extensionGetter, http.build());
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
package run.halo.app.security;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.plugin.extensionpoint.ExtensionGetter;
|
||||
|
||||
/**
|
||||
* A {@link SecurityWebFilterChain} that leverages a {@link ServerWebExchangeMatcher} to
|
||||
* determine which {@link WebFilter} to execute.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public class DynamicMatcherSecurityWebFilterChain implements SecurityWebFilterChain {
|
||||
|
||||
private final SecurityWebFilterChain delegate;
|
||||
|
||||
private final ExtensionGetter extensionGetter;
|
||||
|
||||
public DynamicMatcherSecurityWebFilterChain(ExtensionGetter extensionGetter,
|
||||
SecurityWebFilterChain delegate) {
|
||||
this.delegate = delegate;
|
||||
this.extensionGetter = extensionGetter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> matches(ServerWebExchange exchange) {
|
||||
return delegate.matches(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<WebFilter> getWebFilters() {
|
||||
return Flux.merge(delegate.getWebFilters(), getAdditionalFilters())
|
||||
.sort(new AnnotationAwareOrderComparator());
|
||||
}
|
||||
|
||||
private Flux<WebFilter> getAdditionalFilters() {
|
||||
return extensionGetter.getEnabledExtensionByDefinition(AdditionalWebFilter.class)
|
||||
.map(additionalWebFilter -> new OrderedWebFilter(additionalWebFilter,
|
||||
additionalWebFilter.getOrder())
|
||||
);
|
||||
}
|
||||
|
||||
private record OrderedWebFilter(WebFilter webFilter, int order) implements WebFilter, Ordered {
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Mono<Void> filter(@NonNull ServerWebExchange exchange,
|
||||
@NonNull WebFilterChain chain) {
|
||||
return this.webFilter.filter(exchange, chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package run.halo.app.webfilter;
|
||||
|
||||
import lombok.Setter;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.security.web.server.WebFilterChainProxy;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.plugin.extensionpoint.ExtensionGetter;
|
||||
import run.halo.app.security.AdditionalWebFilter;
|
||||
|
||||
public class AdditionalWebFilterChainProxy implements WebFilter {
|
||||
|
||||
private final ExtensionGetter extensionGetter;
|
||||
|
||||
@Setter
|
||||
private WebFilterChainProxy.WebFilterChainDecorator filterChainDecorator;
|
||||
|
||||
public AdditionalWebFilterChainProxy(ExtensionGetter extensionGetter) {
|
||||
this.extensionGetter = extensionGetter;
|
||||
this.filterChainDecorator = new WebFilterChainProxy.DefaultWebFilterChainDecorator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
return extensionGetter.getEnabledExtensionByDefinition(AdditionalWebFilter.class)
|
||||
.sort(AnnotationAwareOrderComparator.INSTANCE)
|
||||
.cast(WebFilter.class)
|
||||
.collectList()
|
||||
.map(filters -> filterChainDecorator.decorate(chain, filters))
|
||||
.flatMap(decoratedChain -> decoratedChain.filter(exchange));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue