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.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.http.CacheControl;
|
import org.springframework.http.CacheControl;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.MediaType;
|
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.CustomEndpoint;
|
||||||
import run.halo.app.core.extension.endpoint.CustomEndpointsBuilder;
|
import run.halo.app.core.extension.endpoint.CustomEndpointsBuilder;
|
||||||
import run.halo.app.infra.properties.HaloProperties;
|
import run.halo.app.infra.properties.HaloProperties;
|
||||||
|
import run.halo.app.plugin.extensionpoint.ExtensionGetter;
|
||||||
|
import run.halo.app.webfilter.AdditionalWebFilterChainProxy;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class WebFluxConfig implements WebFluxConfigurer {
|
public class WebFluxConfig implements WebFluxConfigurer {
|
||||||
|
@ -200,4 +203,23 @@ public class WebFluxConfig implements WebFluxConfigurer {
|
||||||
ProxyFilter ucProxyFilter() {
|
ProxyFilter ucProxyFilter() {
|
||||||
return new ProxyFilter("/uc/**", haloProp.getUc().getProxy());
|
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.extension.ReactiveExtensionClient;
|
||||||
import run.halo.app.infra.AnonymousUserConst;
|
import run.halo.app.infra.AnonymousUserConst;
|
||||||
import run.halo.app.infra.properties.HaloProperties;
|
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.DefaultUserDetailService;
|
||||||
import run.halo.app.security.DynamicMatcherSecurityWebFilterChain;
|
|
||||||
import run.halo.app.security.authentication.SecurityConfigurer;
|
import run.halo.app.security.authentication.SecurityConfigurer;
|
||||||
import run.halo.app.security.authentication.login.CryptoService;
|
import run.halo.app.security.authentication.login.CryptoService;
|
||||||
import run.halo.app.security.authentication.login.PublicKeyRouteBuilder;
|
import run.halo.app.security.authentication.login.PublicKeyRouteBuilder;
|
||||||
|
@ -60,7 +58,6 @@ public class WebServerSecurityConfig {
|
||||||
RoleService roleService,
|
RoleService roleService,
|
||||||
ObjectProvider<SecurityConfigurer> securityConfigurers,
|
ObjectProvider<SecurityConfigurer> securityConfigurers,
|
||||||
ServerSecurityContextRepository securityContextRepository,
|
ServerSecurityContextRepository securityContextRepository,
|
||||||
ExtensionGetter extensionGetter,
|
|
||||||
ReactiveExtensionClient client,
|
ReactiveExtensionClient client,
|
||||||
PatJwkSupplier patJwkSupplier) {
|
PatJwkSupplier patJwkSupplier) {
|
||||||
|
|
||||||
|
@ -92,7 +89,7 @@ public class WebServerSecurityConfig {
|
||||||
// Integrate with other configurers separately
|
// Integrate with other configurers separately
|
||||||
securityConfigurers.orderedStream()
|
securityConfigurers.orderedStream()
|
||||||
.forEach(securityConfigurer -> securityConfigurer.configure(http));
|
.forEach(securityConfigurer -> securityConfigurer.configure(http));
|
||||||
return new DynamicMatcherSecurityWebFilterChain(extensionGetter, http.build());
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@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