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

Play Framework中如何在pac4j自定义Authorizer中获取请求体

问题解决:Play Framework + pac4j 自定义Authorizer无法获取请求负载

问题原因

Play Framework的请求体是一次性读取的流,pac4j的PlayWebContext默认不会主动读取并缓存请求体。如果在调用getRequestContent()时请求体尚未被读取(或未被缓存),就会返回null。而请求头是直接从请求对象中获取,不存在流消耗的问题,所以能正常读取。

解决方案

方案1:在Authorizer中直接读取Play请求体

将WebContext强转为PlayWebContext,直接获取Play原生的Http.Request对象,手动读取请求体内容:

package v2;

import org.pac4j.core.authorization.authorizer.ProfileAuthorizer;
import org.pac4j.core.context.WebContext;
import org.pac4j.core.context.session.SessionStore;
import org.pac4j.core.profile.UserProfile;
import org.pac4j.play.PlayWebContext;
import play.mvc.Http;

import java.nio.charset.StandardCharsets;
import java.util.List;

public class OPAAuthorizer extends ProfileAuthorizer {
    @Override
    public boolean isAuthorized(WebContext context, SessionStore sessionStore, List<UserProfile> profiles) {
        System.out.println(context.getRequestHeader("Authorization"));
        
        if (context instanceof PlayWebContext) {
            PlayWebContext playWebContext = (PlayWebContext) context;
            Http.Request playRequest = playWebContext.getRequest();
            Http.Body requestBody = playRequest.body();
            
            // 根据请求类型读取内容
            String requestContent;
            if (requestBody.isJson()) {
                requestContent = requestBody.asJson().toString();
            } else if (requestBody.isText()) {
                requestContent = requestBody.asText();
            } else {
                // 处理二进制或其他类型请求体
                byte[] bodyBytes = requestBody.asBytes().orElse(new byte[0]);
                requestContent = new String(bodyBytes, StandardCharsets.UTF_8);
            }
            
            System.out.println(requestContent);
        }
        
        return true;
    }

    @Override
    protected boolean isProfileAuthorized(WebContext context, SessionStore sessionStore, UserProfile profile) {
        return false;
    }
}

方案2:提前缓存请求体(避免后续流消耗问题)

如果后续控制器还需要读取请求体,直接读取会导致流被消耗,因此可以通过自定义Play Action提前缓存请求体,让请求体可重复读取:

  1. 创建缓存请求体的Action:
import play.mvc.Action;
import play.mvc.Http;
import play.mvc.Result;

import java.util.concurrent.CompletionStage;

public class CachedRequestBodyAction extends Action.Simple {
    @Override
    public CompletionStage<Result> call(Http.Request request) {
        // 读取请求体字节并缓存,生成可重复读取的请求对象
        Http.Request cachedRequest = request.body().asBytes()
                .map(bytes -> request.withBody(Http.BodyParser.Default.parse(bytes)))
                .orElse(request);
        return delegate.call(cachedRequest);
    }
}
  1. 在路由中应用该Action(或全局注册):
# 给需要验证的路由添加缓存Action
+CachedRequestBodyAction
POST    /api/your-endpoint   v2.YourController.yourMethod

应用该Action后,无论是Authorizer还是后续控制器,都能正常读取请求体内容。

注意事项

  • 如果请求体过大,直接读取为字符串可能会导致内存占用过高,需根据业务场景调整处理逻辑。
  • 确保请求的Content-Type设置正确,比如JSON请求需设置Content-Type: application/json,否则isJson()判断会失效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 13:45:15