Jersey客户端BASIC认证GET请求出现预请求401错误
问题核心
使用Payara 5.2022.3或Payara 4/Jersey-2.25.1时,受BASIC认证保护的REST端点出现异常:Jersey客户端发起GET请求时,会先发送不带Authorization头的请求,收到401后才补发带认证头的请求;而curl执行相同请求则直接携带认证头并返回200。代码未改动,推测是Java版本更新引发的问题。
原因定位
从客户端代码和日志来看,问题出在Jersey客户端的认证配置:
clientConfig.register(HttpAuthenticationFeature.basicBuilder().nonPreemptive().credentials(username, password).build());
这里明确使用了nonPreemptive()模式,该模式的逻辑就是先发送无认证头的请求,等待服务器返回WWW-Authenticate响应后,再携带认证头重试。而curl默认使用**预认证(Preemptive)**模式,会直接在首次请求中携带Authorization头,这就是两者行为差异的直接原因。
至于之前正常现在异常,大概率是Java更新后,Jersey底层依赖的HttpUrlConnection(从日志User-Agent: Jersey/2.25.1 (HttpUrlConnection 1.8.0_352)可看出)的默认行为发生变化,或者Payara服务器对未认证请求的响应逻辑调整,导致原本可能隐式生效的预认证不再起作用。
解决方案
将Jersey客户端的认证模式改为预认证,去掉nonPreemptive()调用即可:
// 移除nonPreemptive(),使用默认的预认证模式 clientConfig.register(HttpAuthenticationFeature.basicBuilder().credentials(username, password).build());
或者显式指定预认证:
clientConfig.register(HttpAuthenticationFeature.basicBuilder().preemptive().credentials(username, password).build());
修改后,Jersey客户端会在首次请求时直接携带Authorization头,和curl的行为一致,避免401重试。
验证说明
修改配置后重新发起请求,客户端日志将只会出现一次请求,直接携带Authorization头并返回200,与curl的执行结果一致。
内容的提问来源于stack exchange,提问作者John Manko

