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提前缓存请求体,让请求体可重复读取:
- 创建缓存请求体的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); } }
- 在路由中应用该Action(或全局注册):
# 给需要验证的路由添加缓存Action +CachedRequestBodyAction POST /api/your-endpoint v2.YourController.yourMethod
应用该Action后,无论是Authorizer还是后续控制器,都能正常读取请求体内容。
注意事项
- 如果请求体过大,直接读取为字符串可能会导致内存占用过高,需根据业务场景调整处理逻辑。
- 确保请求的
Content-Type设置正确,比如JSON请求需设置Content-Type: application/json,否则isJson()判断会失效。
内容的提问来源于stack exchange,提问作者Bill Goldberg
相关产品推荐
相关产品推荐

