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

POST请求场景下OIDC授权丢失请求体问题及Quarkus调试

问题:OIDC授权流程丢失POST请求体(Quarkus 2.16.12环境)

场景描述

两个应用通过iframe集成,应用A向应用B发起POST表单请求:

  • 用户已在应用B完成授权时,流程正常
  • 用户未授权时,流程出现异常:
    1. 应用A构造发往应用B的POST请求
    2. 用户被重定向至微软OIDC身份提供商完成授权
    3. 授权完成后重定向回应用B的回调URL,再跳转至原请求页面
    4. 页面空白,原因是跳转采用GET请求,未携带原POST请求体

现有尝试及问题

尝试通过优先级1000的RouteFilter存储原POST请求体,但代码中始终无法获取到请求体,代码如下:

@Inject
CurrentVertxRequest request;

@RouteFilter(1000)
void myFilter(RoutingContext rc) {
    Buffer body = request.getCurrent().request().body().result();
    rc.response().headersEndHandler(new Handler<Void>() {
        @Override
        public void handle(Void event) {
            int statusCode = rc.response().getStatusCode();
            if (statusCode == 401) {
                rc.redirect("back").result();
            }
            if (rc.request().path().endsWith(LaunchHtml.PATH)) {
                if (statusCode == 302 || statusCode == 401 || statusCode == 403) {
                    if (result != null) {
                        String key = UUID.randomUUID().toString();
                        rc.response().addCookie(new CookieImpl(PREVIOUS_PAYLOAD, key));
                        PAYLOAD_STORE.put(key, body.toString());
                    }
                }
            }
        }
    });
    rc.next();
}

问题核心:直接调用body().result()是同步阻塞方式,此时Vert.x尚未完成请求体的异步读取,导致获取到空值。

解决方案

1. 异步读取请求体

改用Vert.x的异步handler读取请求体,确保在获取到完整请求体后再执行后续逻辑。

2. 存储请求体并关联会话

将请求体存储到分布式缓存(如Quarkus的Infinispan)或会话中,通过Cookie标记关联的存储键。

3. 授权回调后恢复请求

在应用B的OIDC回调端点,检查是否存在存储的请求体,若存在则重新构造POST请求或填充到目标页面。

修正后的RouteFilter代码

import io.quarkus.vertx.http.runtime.CurrentVertxRequest;
import io.vertx.ext.web.RoutingContext;
import io.vertx.core.buffer.Buffer;
import io.vertx.ext.web.Cookie;
import io.vertx.ext.web.CookieImpl;
import java.util.UUID;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

@ApplicationScoped
public class PostPayloadFilter {

    private static final String PREVIOUS_PAYLOAD_COOKIE = "PREVIOUS_PAYLOAD";
    // 生产环境建议用分布式缓存替代HashMap,避免集群环境数据不一致
    private final java.util.Map<String, String> PAYLOAD_STORE = new java.util.concurrent.ConcurrentHashMap<>();

    @Inject
    CurrentVertxRequest currentVertxRequest;

    @RouteFilter(1000)
    void capturePostPayload(RoutingContext rc) {
        // 仅处理POST请求
        if (!"POST".equalsIgnoreCase(rc.request().method().name())) {
            rc.next();
            return;
        }

        // 异步读取请求体
        rc.request().bodyHandler(body -> {
            // 绑定响应结束后的处理逻辑
            rc.response().headersEndHandler(v -> {
                int statusCode = rc.response().getStatusCode();
                // 仅在需要重定向授权的场景下存储请求体
                if ((statusCode == 401 || statusCode == 302) && rc.request().path().endsWith(LaunchHtml.PATH)) {
                    if (body.length() > 0) {
                        String payloadKey = UUID.randomUUID().toString();
                        PAYLOAD_STORE.put(payloadKey, body.toString());
                        // 设置Cookie,注意配置路径和有效期
                        Cookie cookie = CookieImpl.create(PREVIOUS_PAYLOAD_COOKIE, payloadKey)
                                .setPath("/")
                                .setMaxAge(300) // 5分钟有效期
                                .setHttpOnly(true);
                        rc.response().addCookie(cookie);
                    }
                }
            });
            // 将读取到的请求体放回上下文,确保后续处理器能正常获取
            rc.setBody(body);
            rc.next();
        });
    }
}

补充说明

  • 存储方案:生产环境建议使用Quarkus集成的Infinispan或Redis替代ConcurrentHashMap,避免集群部署时的节点间数据不一致问题。
  • 回调恢复:在OIDC回调的处理方法中,读取Cookie中的PREVIOUS_PAYLOAD键,从存储中取出请求体,然后重定向到目标页面时通过POST方式提交,或直接填充表单数据。
  • Cookie安全:设置HttpOnly和Secure属性(HTTPS环境下),避免XSS攻击风险。

内容的提问来源于stack exchange,提问作者Jozef Dropčo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 09:32:45