Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide extension points for authentication-related web filters #5386

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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 {

}
Original file line number Diff line number Diff line change
@@ -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 {

}
Original file line number Diff line number Diff line change
@@ -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 {

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* @since 2.0.0
*/
@Component
@Deprecated(forRemoval = true)
public class ExtensionComponentsFinder {
public static final String SYSTEM_PLUGIN_ID = "system";
private final PluginManager pluginManager;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package run.halo.app.plugin.extensionpoint;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
Expand Down Expand Up @@ -86,6 +87,15 @@ public <T extends ExtensionPoint> Flux<T> getEnabledExtensionByDefinition(
});
}

@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
<T extends ExtensionPoint> List<T> getAllExtensions(Class<T> extensionPoint) {
Stream<T> pluginExtsStream = pluginManager.getExtensions(extensionPoint)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,13 @@ public interface ExtensionGetter {
* the {@link ExtensionPointDefinition}.
*/
<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);
}
Original file line number Diff line number Diff line change
@@ -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));
}
}

}
77 changes: 77 additions & 0 deletions docs/extension-points/authentication.md
Original file line number Diff line number Diff line change
@@ -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 留下机会。

后续会根据需求实现其他认证相关的扩展点。