我们需要构建一个支持传统定制(非 JWT)承载令牌身份验证标头的服务:
Authorization: bespoke ....
和 JWT Bearer 令牌身份验证标头:
Authorization: Bearer ......
使用 Webflux 和 oauth2-resource-server 实现将 Bespoke + JWT 验证组合为 Spring Boot 3.2 中后备的 Bean 最简洁的方法是什么?
我期望一个自定义 bean 组成 DefaultBearerTokenResolver 和一个定制解析器,或者一个自定义 ReactiveAuthenticationManagerResolver bean 执行相同的操作,但我不确定哪个会更简单。
如果两者之间的安全配置明显不同,您应该定义不同的
SecurityWebFilterChain
beans:
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
SecurityWebFilterChain bespokeFilterChain(ServerHttpSecurity http) {
http.securityMatcher((ServerWebExchange exchange) -> {
final var isBespoke = Optional.ofNullable(exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION)).map(auth -> auth.toLowerCase().startsWith("bespoke ") ? true : false).orElse(false);
return isBespoke ? ServerWebExchangeMatcher.MatchResult.match() : ServerWebExchangeMatcher.MatchResult.notMatch();
});
...
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
SecurityWebFilterChain bearerFilterChain(ServerHttpSecurity http) {
http.securityMatcher((ServerWebExchange exchange) -> {
final var isBearer = Optional.ofNullable(exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION)).map(auth -> auth.toLowerCase().startsWith("bearer ") ? true : false).orElse(false);
return isBearer ? ServerWebExchangeMatcher.MatchResult.match() : ServerWebExchangeMatcher.MatchResult.notMatch();
});
...
}
...
@Bean
@Order(Ordered.LOWEST_PRECEDENCE)
SecurityWebFilterChain defaultFilterChain(ServerHttpSecurity http) {
// No http.securityMatcher here
// Processes requests which were not matched in filter chains with higher precedence (for instance anonymous requests)
...
}
这里我演示了
Authorization
标头上的匹配器,但您可以匹配请求中的任何内容(路径、媒体类型、源 IP 等)。
当不同身份验证策略之间的安全要求不同时,这非常有用,例如,当混合
oauth2Login()
(需要会话、CSRF 保护,并且通常配置 302 redirect to login
用于对受保护资源的未经授权的请求)和 oauth2ResourceServer()
时,这非常有用(通常是无状态的,401 unauthorized
)在单个应用程序中。
如果除了身份验证管理器之外的所有内容都是相同的,那么您可能想要实现自己的
ReactiveAuthenticationManagerResolver<ServerWebExchange>
:
@Bean
SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http, ReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver) {
http.oauth2ResourceServer(oauth2 -> oauth2.authenticationManagerResolver(authenticationManagerResolver));
...
}
@Component
static class MyReactiveAuthenticationManagerResolver implements ReactiveAuthenticationManagerResolver<ServerWebExchange> {
private final ReactiveAuthenticationManager bespokeAuthManager;
private final ReactiveAuthenticationManager bearerAuthManager;
private final ReactiveAuthenticationManager defaultAuthManager;
MyReactiveAuthenticationManagerResolver() {
...
}
@Override
public Mono<ReactiveAuthenticationManager> resolve(ServerWebExchange context) {
final var authHeader = Optional.ofNullable(context.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION)).map(auth -> auth.toLowerCase()).orElse("");
if(authHeader.startsWith("bespoke ")) {
return Mono.just(bespokeAuthManager);
}
if(authHeader.startsWith("bearer ")) {
return Mono.just(bearerAuthManager);
}
return Mono.just(defaultAuthManager);
}
}