Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: enhance additional web filter with scope and portal interception
Browse files Browse the repository at this point in the history
guqing committed Feb 1, 2024
1 parent 5b93667 commit 1e3986c
Showing 3 changed files with 38 additions and 4 deletions.
10 changes: 10 additions & 0 deletions api/src/main/java/run/halo/app/security/AdditionalWebFilter.java
Original file line number Diff line number Diff line change
@@ -22,4 +22,14 @@ public interface AdditionalWebFilter extends WebFilter, ExtensionPoint, Ordered
default int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}

default Scope getScope() {
return Scope.PROTECTED_API;
}

enum Scope {
PROTECTED_API,
PORTAL,
ALL
}
}
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@
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.AdditionalWebFilter;
import run.halo.app.security.DefaultUserDetailService;
import run.halo.app.security.DynamicMatcherSecurityWebFilterChain;
import run.halo.app.security.authentication.SecurityConfigurer;
@@ -92,14 +93,16 @@ SecurityWebFilterChain apiFilterChain(ServerHttpSecurity http,
// Integrate with other configurers separately
securityConfigurers.orderedStream()
.forEach(securityConfigurer -> securityConfigurer.configure(http));
return new DynamicMatcherSecurityWebFilterChain(extensionGetter, http.build());
return new DynamicMatcherSecurityWebFilterChain(extensionGetter, http.build(),
Set.of(AdditionalWebFilter.Scope.PROTECTED_API));
}

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
SecurityWebFilterChain portalFilterChain(ServerHttpSecurity http,
ServerSecurityContextRepository securityContextRepository,
HaloProperties haloProperties) {
HaloProperties haloProperties,
ExtensionGetter extensionGetter) {
var pathMatcher = pathMatchers(HttpMethod.GET, "/**");
var mediaTypeMatcher = new MediaTypeServerWebExchangeMatcher(MediaType.TEXT_HTML);
mediaTypeMatcher.setIgnoredMediaTypes(Set.of(MediaType.ALL));
@@ -126,7 +129,8 @@ SecurityWebFilterChain portalFilterChain(ServerHttpSecurity http,
new HaloAnonymousAuthenticationWebFilter("portal", AnonymousUserConst.PRINCIPAL,
AuthorityUtils.createAuthorityList(AnonymousUserConst.Role),
securityContextRepository)));
return http.build();
return new DynamicMatcherSecurityWebFilterChain(extensionGetter, http.build(),
Set.of(AdditionalWebFilter.Scope.PORTAL));
}

@Bean
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package run.halo.app.security;

import java.util.Set;
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.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
@@ -25,10 +28,23 @@ public class DynamicMatcherSecurityWebFilterChain implements SecurityWebFilterCh

private final ExtensionGetter extensionGetter;

private final Set<AdditionalWebFilter.Scope> matchScopes;

/**
* Creates an aggregated {@link SecurityWebFilterChain} using the provided original and
* additional filters.
*
* @param matchScopes Only matched the given scopes will be added to the filter chain
*/
public DynamicMatcherSecurityWebFilterChain(ExtensionGetter extensionGetter,
SecurityWebFilterChain delegate) {
SecurityWebFilterChain delegate,
Set<AdditionalWebFilter.Scope> matchScopes) {
Assert.isTrue(!CollectionUtils.isEmpty(matchScopes), "Match scopes must not be empty");
Assert.notNull(extensionGetter, "Extension getter must not be null");
Assert.notNull(delegate, "Delegate must not be null");
this.delegate = delegate;
this.extensionGetter = extensionGetter;
this.matchScopes = matchScopes;
}

@Override
@@ -44,6 +60,10 @@ public Flux<WebFilter> getWebFilters() {

private Flux<WebFilter> getAdditionalFilters() {
return extensionGetter.getEnabledExtensionByDefinition(AdditionalWebFilter.class)
.filter(additionalWebFilter -> {
var scope = additionalWebFilter.getScope();
return scope == AdditionalWebFilter.Scope.ALL || matchScopes.contains(scope);
})
.map(additionalWebFilter -> new OrderedWebFilter(additionalWebFilter,
additionalWebFilter.getOrder())
);

0 comments on commit 1e3986c

Please sign in to comment.