Skip to content

Commit

Permalink
Provide extension points for authentication-related web filters (#5386)
Browse files Browse the repository at this point in the history
#### 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 #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 #5379

#### Special notes for your reviewer:

TBD.

#### Does this PR introduce a user-facing change?

```release-note
None
```
  • Loading branch information
JohnNiang committed Feb 23, 2024
1 parent 50fbe37 commit bbe79ba
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 0 deletions.
@@ -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 {

}
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
@@ -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
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);
}
@@ -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
@@ -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 留下机会。

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

0 comments on commit bbe79ba

Please sign in to comment.