java - WebClient ExchangeFilterFunction 缓存来自 ThreadLocal 的值
问题描述
在将 Spring MVC 与 Webflux 的 ThreadLocal 和 WebClient 一起使用时,我遇到了以下问题。
我的任务是:
拦截用户对我的应用程序的请求,并从中获取所有标头并将其保存在 ThreadLocal 中。
之后,当我的应用程序通过 WebClient 调用另一个服务时,在 ExchangeFilterFunction 中拦截此请求,并用 p.1 中的 Authorization 标头补充它。
当我完成处理用户的请求时,我会清除上下文。
我使用我的自定义类“RequestContext”将标头存储在 ThreadLocal 中:
public class RequestContext {
private HttpHeaders requestHeaders;
private String jwt;
private static final String BEARER_PREFIX = "Bearer ";
public RequestContext(HttpHeaders httpHeaders) {
this.requestHeaders = httpHeaders;
if (Objects.nonNull(httpHeaders)) {
init();
}
}
private void init() {
if (Objects.nonNull(requestHeaders)) {
extractJwt();
}
}
private void extractJwt() {
var jwtHeader = requestHeaders.getFirst(HttpHeaders.AUTHORIZATION);
if (StringUtils.isNotBlank(jwtHeader) && jwtHeader.startsWith(BEARER_PREFIX)) {
jwt = jwtHeader.substring(7);
}
}
}
我使用我的自定义类“RequestContextService”来处理 ThreadLocal:
public class RequestContextService {
private static final ThreadLocal<RequestContext> CONTEXT = new InheritableThreadLocal<>();
public void init(RequestContext requestContext) {
if (Objects.isNull(CONTEXT.get())) {
CONTEXT.set(requestContext);
} else {
log.error("#init: Context init error");
}
}
public RequestContext get() {
return CONTEXT.get();
}
public void clear() {
CONTEXT.remove();
}
}
我的应用是一个 WebMvc 应用。为了完成第 1 步,我使用 HandlerInterceptor 拦截请求并将所有标头设置为 Threadlocal。
public class HeaderInterceptor implements HandlerInterceptor {
private final RequestContextService requestContextService;
@Override
public boolean preHandle(@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull Object handler) {
if (Objects.equals(request.getDispatcherType(), DispatcherType.REQUEST)) {
var headers = new ServletServerHttpRequest(request).getHeaders();
requestContextService.init(new RequestContext(headers));
}
return true;
}
@Override
public void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
@NonNull Object handler, Exception ex) {
requestContextService.clear();
}
}
如您所见,在每次请求后,我都会调用“requestContextService.clear()”方法来清除 ThreadLocal。
为了执行第二步,我使用了 ExchangeFilterFunction,在这里我转向 threadlocal 并从那里获取标题。
public class SamlExchangeFilterFunction implements ExchangeFilterFunction {
private final RequestContextService requestContextService;
private static final ClientResponse UNAUTHORIZED_CLIENT_RESPONSE =
ClientResponse.create(HttpStatus.UNAUTHORIZED).build();
@Override
public @NotNull Mono<ClientResponse> filter(@NotNull ClientRequest request, @NotNull ExchangeFunction next) {
var jwt = requestContextService.get().getJwt();
if (StringUtils.isNoneBlank(jwt)) {
var clientRequest = ClientRequest.from(request)
.headers(httpHeaders -> httpHeaders.set(SAML_HEADER_NAME, jwt))
.build();
return next.exchange(clientRequest);
}
return Mono.just(UNAUTHORIZED_CLIENT_RESPONSE);
}
}
问题是 SamlExchangeFilterFunction 只能正常工作一次。
在对应用程序的第一次请求时,一切正常。但是对于具有不同授权标头的进一步请求,ExchangeFilterFunction 似乎缓存了第一个请求的值并替换它,尽管 threadlocal 本身包含授权标头的完全不同的含义。
解决方案
推荐阅读
- php - 如何生成没有错误的 Blockcypher 合约?
- php - Laravel 将一个可调用的 onDelete 附加到 eloquent 模型
- amazon-web-services - 我想将多层 Web 应用程序部署到 AWS,但不知道如何设置
- reactjs - React JS 如何让一个按钮创建另一个按钮
- api - 为什么“服务器上的 IIS”的行为与“本地 IIS”和“IIS Express”的行为不同?(ASP.NET Core + Angular + Windows 身份验证)
- html - 如何在 Google 网站上为 Google 表格创建外部参考页面
- cng - 未找到加密提供程序开发工具包
- reactjs - Jotai + React:意外的重新渲染和意外的陈旧状态值
- html - 当我将边栏中的内容设置为显示时,它不再随浏览器窗口一起缩小:grid
- java - 控制台整体执行程序的问题