mirror of https://github.com/halo-dev/halo
Provide extension points for authentication-related web filters (#5386)
#### What type of PR is this? /kind feature /area core /area plugin /milestone 2.13.x #### What this PR does / why we need it: See https://github.com/halo-dev/halo/issues/5379 for more. This PR provides three extension points: - FormLoginSecurityWebFilter - AuthenticationSecurityWebFilter - AnonymousAuthenticationSecurityWebFilter which could be extended by plugins easily. #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/5379 #### Special notes for your reviewer: TBD. #### Does this PR introduce a user-facing change? ```release-note None ```pull/5400/head
parent
50fbe37be8
commit
bbe79bac10
|
@ -0,0 +1,13 @@
|
||||||
|
package run.halo.app.security;
|
||||||
|
|
||||||
|
import org.pf4j.ExtensionPoint;
|
||||||
|
import org.springframework.web.server.WebFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security web filter for anonymous authentication.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
public interface AnonymousAuthenticationSecurityWebFilter extends WebFilter, ExtensionPoint {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package run.halo.app.security;
|
||||||
|
|
||||||
|
import org.pf4j.ExtensionPoint;
|
||||||
|
import org.springframework.web.server.WebFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security web filter for normal authentication.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
public interface AuthenticationSecurityWebFilter extends WebFilter, ExtensionPoint {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package run.halo.app.security;
|
||||||
|
|
||||||
|
import org.pf4j.ExtensionPoint;
|
||||||
|
import org.springframework.web.server.WebFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security web filter for form login.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
public interface FormLoginSecurityWebFilter extends WebFilter, ExtensionPoint {
|
||||||
|
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import org.springframework.stereotype.Component;
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
public class ExtensionComponentsFinder {
|
public class ExtensionComponentsFinder {
|
||||||
public static final String SYSTEM_PLUGIN_ID = "system";
|
public static final String SYSTEM_PLUGIN_ID = "system";
|
||||||
private final PluginManager pluginManager;
|
private final PluginManager pluginManager;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package run.halo.app.plugin.extensionpoint;
|
package run.halo.app.plugin.extensionpoint;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -86,6 +87,15 @@ public class DefaultExtensionGetter implements ExtensionGetter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends ExtensionPoint> Flux<T> getExtensions(Class<T> extensionPointClass) {
|
||||||
|
var extensions = new ArrayList<>(pluginManager.getExtensions(extensionPointClass));
|
||||||
|
applicationContext.getBeanProvider(extensionPointClass)
|
||||||
|
.orderedStream()
|
||||||
|
.forEach(extensions::add);
|
||||||
|
return Flux.fromIterable(extensions);
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
<T extends ExtensionPoint> List<T> getAllExtensions(Class<T> extensionPoint) {
|
<T extends ExtensionPoint> List<T> getAllExtensions(Class<T> extensionPoint) {
|
||||||
Stream<T> pluginExtsStream = pluginManager.getExtensions(extensionPoint)
|
Stream<T> pluginExtsStream = pluginManager.getExtensions(extensionPoint)
|
||||||
|
|
|
@ -34,4 +34,13 @@ public interface ExtensionGetter {
|
||||||
* the {@link ExtensionPointDefinition}.
|
* the {@link ExtensionPointDefinition}.
|
||||||
*/
|
*/
|
||||||
<T extends ExtensionPoint> Flux<T> getEnabledExtensionByDefinition(Class<T> extensionPoint);
|
<T extends ExtensionPoint> Flux<T> getEnabledExtensionByDefinition(Class<T> extensionPoint);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all extensions according to extension point class.
|
||||||
|
*
|
||||||
|
* @param extensionPointClass extension point class
|
||||||
|
* @param <T> type of extension point
|
||||||
|
* @return a bunch of extension points.
|
||||||
|
*/
|
||||||
|
<T extends ExtensionPoint> Flux<T> getExtensions(Class<T> extensionPointClass);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package run.halo.app.security;
|
||||||
|
|
||||||
|
import static org.springframework.security.config.web.server.SecurityWebFiltersOrder.ANONYMOUS_AUTHENTICATION;
|
||||||
|
import static org.springframework.security.config.web.server.SecurityWebFiltersOrder.AUTHENTICATION;
|
||||||
|
import static org.springframework.security.config.web.server.SecurityWebFiltersOrder.FORM_LOGIN;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.pf4j.ExtensionPoint;
|
||||||
|
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||||
|
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||||
|
import org.springframework.security.web.server.WebFilterChainProxy;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
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.authentication.SecurityConfigurer;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SecurityWebFiltersConfigurer implements SecurityConfigurer {
|
||||||
|
|
||||||
|
private final ExtensionGetter extensionGetter;
|
||||||
|
|
||||||
|
public SecurityWebFiltersConfigurer(ExtensionGetter extensionGetter) {
|
||||||
|
this.extensionGetter = extensionGetter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(ServerHttpSecurity http) {
|
||||||
|
http
|
||||||
|
.addFilterAt(
|
||||||
|
new SecurityWebFilterChainProxy(FormLoginSecurityWebFilter.class), FORM_LOGIN
|
||||||
|
)
|
||||||
|
.addFilterAt(
|
||||||
|
new SecurityWebFilterChainProxy(AuthenticationSecurityWebFilter.class),
|
||||||
|
AUTHENTICATION
|
||||||
|
)
|
||||||
|
.addFilterAt(
|
||||||
|
new SecurityWebFilterChainProxy(AnonymousAuthenticationSecurityWebFilter.class),
|
||||||
|
ANONYMOUS_AUTHENTICATION
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SecurityWebFilterChainProxy implements WebFilter {
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private WebFilterChainProxy.WebFilterChainDecorator filterChainDecorator;
|
||||||
|
|
||||||
|
private final Class<? extends ExtensionPoint> extensionPointClass;
|
||||||
|
|
||||||
|
public SecurityWebFilterChainProxy(Class<? extends ExtensionPoint> extensionPointClass) {
|
||||||
|
this.extensionPointClass = extensionPointClass;
|
||||||
|
this.filterChainDecorator = new WebFilterChainProxy.DefaultWebFilterChainDecorator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||||
|
return extensionGetter.getExtensions(this.extensionPointClass)
|
||||||
|
.sort(AnnotationAwareOrderComparator.INSTANCE)
|
||||||
|
.cast(WebFilter.class)
|
||||||
|
.collectList()
|
||||||
|
.map(filters -> filterChainDecorator.decorate(chain, filters))
|
||||||
|
.flatMap(decoratedChain -> decoratedChain.filter(exchange));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
# Halo 认证扩展点
|
||||||
|
|
||||||
|
此前,Halo 提供了 AdditionalWebFilter 作为扩展点供插件扩展认证相关的功能。但是近期我们明确了 AdditionalWebFilter
|
||||||
|
的使用用途,故不再作为认证的扩展点。
|
||||||
|
|
||||||
|
目前,Halo 提供了三种认证扩展点:表单登录认证、普通认证和匿名认证。
|
||||||
|
|
||||||
|
## 表单登录(FormLogin)
|
||||||
|
|
||||||
|
示例如下:
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import org.springframework.web.server.WebFilterChain;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import run.halo.app.security.FormLoginSecurityWebFilter;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MyFormLoginSecurityWebFilter implements FormLoginSecurityWebFilter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||||
|
// Do your logic here
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
## 普通认证(Authentication)
|
||||||
|
|
||||||
|
示例如下:
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import org.springframework.web.server.WebFilterChain;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import run.halo.app.security.AuthenticationSecurityWebFilter;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MyAuthenticationSecurityWebFilter implements AuthenticationSecurityWebFilter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||||
|
// Do your logic here
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 匿名认证(Anonymous Authentication
|
||||||
|
|
||||||
|
示例如下:
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import org.springframework.web.server.WebFilterChain;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import run.halo.app.security.AnonymousAuthenticationSecurityWebFilter;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MyAnonymousAuthenticationSecurityWebFilter
|
||||||
|
implements AnonymousAuthenticationSecurityWebFilter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||||
|
// Do your logic here
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
我们在实现扩展点的时候需要注意:如果当前请求不满足认证条件,请一定要调用 `chain.filter(exchange)`,给其他 filter 留下机会。
|
||||||
|
|
||||||
|
后续会根据需求实现其他认证相关的扩展点。
|
Loading…
Reference in New Issue