You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Mono中的JWT增强非过时版ReactiveElasticsearchClient请求

为Spring Data Elasticsearch ELC客户端添加动态JWT授权头的非阻塞方案

由于ELC包(org.springframework.data.elasticsearch.client.elc)基于Apache HttpAsyncClient,无法直接像ERHLC那样用WebClient过滤器处理非阻塞的JWT获取,推荐使用客户端装饰器模式,在每个请求链路中动态从ReactiveSecurityContextHolder获取JWT并修改请求头,具体步骤如下:

1. 实现带认证的ReactiveElasticsearchClient包装类

创建一个装饰器类,委托原生客户端的方法,同时在执行请求前插入JWT头的获取逻辑:

import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.http.HttpHeaders;
import org.elasticsearch.client.RequestOptions;
import reactor.core.publisher.Mono;

@Component
public class AuthenticatedReactiveElasticsearchClient implements ReactiveElasticsearchClient {

    private final ReactiveElasticsearchClient delegate;

    public AuthenticatedReactiveElasticsearchClient(ReactiveElasticsearchClient delegate) {
        this.delegate = delegate;
    }

    @Override
    public <T> Mono<T> execute(ClientCallback<Mono<T>> callback) {
        return ReactiveSecurityContextHolder.getContext()
                // 从SecurityContext中提取JWT令牌
                .map(ctx -> (JwtAuthenticationToken) ctx.getAuthentication())
                .map(JwtAuthenticationToken::getToken)
                .map(jwt -> "Bearer " + jwt.getTokenValue())
                // 处理未认证的情况
                .defaultIfEmpty("")
                .flatMap(authHeader -> {
                    // 构建带授权头的RequestOptions
                    RequestOptions authOptions = RequestOptions.DEFAULT.toBuilder()
                            .addHeader(HttpHeaders.AUTHORIZATION, authHeader)
                            .build();
                    // 用自定义RequestOptions执行请求
                    return callback.execute(new ClientConfiguration() {
                        @Override
                        public RequestOptions toRequestOptions() {
                            return authOptions;
                        }

                        @Override
                        public HttpHeaders getHeaders() {
                            return delegate.getConfiguration().getHeaders();
                        }

                        // 其他ClientConfiguration方法按需委托或实现默认逻辑
                    });
                })
                // 兜底:如果SecurityContext为空,用原配置执行请求
                .switchIfEmpty(callback.execute(delegate.getConfiguration()));
    }

    // 委托其他客户端方法(如indices、search等)
    @Override
    public ReactiveIndicesClient indices() {
        return delegate.indices();
    }

    @Override
    public <T> ReactiveSearchClient<T> search(Class<T> clazz) {
        return delegate.search(clazz);
    }

    // 覆盖其他必要方法,均委托给原生客户端即可
}

2. 配置包装类为主要客户端Bean

在配置类中,将自定义的包装类注册为ReactiveElasticsearchClient的Bean,替代原生实例:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient;

@Configuration
public class ElasticsearchAuthConfig {

    @Bean
    public ReactiveElasticsearchClient authenticatedReactiveElasticsearchClient(ReactiveElasticsearchClient nativeClient) {
        return new AuthenticatedReactiveElasticsearchClient(nativeClient);
    }
}

方案说明

  • 该方案完全基于Reactor非阻塞模型,通过ReactiveSecurityContextHolder.getContext()异步获取JWT,不会阻塞线程
  • 利用装饰器模式,无需修改原生客户端的底层配置,对业务代码无侵入
  • 处理了未认证场景的兜底逻辑,避免请求因缺少头而失败
  • 所有请求都会动态生成带授权头的RequestOptions,保证每个请求的头都是当前上下文的有效JWT

内容的提问来源于stack exchange,提问作者GeBeater

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 18:34:59