CLI带证书Curl请求正常,PHP实现却返回400错误
PHP CURL请求OAuth2令牌返回400 Bad Request(CLI正常,Docker环境异常)
问题概述
- 一套带客户端证书的OAuth2令牌获取请求,在CLI下用curl命令可正常执行并拿到令牌,但转成PHP的CURL代码后,请求始终返回
400 Bad Request - API强制要求请求携带客户端证书,对比两者的verbose输出,发现TLS握手阶段存在明显差异,曾怀疑
CURLOPT_SSLCERT和CURLOPT_SSLKEY是否与CLI的--cert、--key参数对应 - 后续通过Wireshark抓包发现,请求序列末尾存在TCP重传差异;且在Docker环境外运行PHP代码无任何错误,因此推测问题根源在于Docker开发环境配置
原始代码对比
CLI curl命令
curl --request POST --url https://sysorgoauth2.test.XXX.TLD/oauth2/v1/sysorg/token --cert /app/keys/eid.crt.pem --key /app/keys/eid.key.pem --header "Content-Type: application/x-www-form-urlencoded" --verbose -d "grant_type=client_credentials&client_id=<ID>&client_secret=<SECRET>&scope=<SCOPE>"
PHP CURL代码
$headers = [ 'User-Agent: curl/7.74.0 via PHP', 'Content-Type: application/x-www-form-urlencoded', 'Accept: */*', ]; $url = 'https://sysorgoauth2.test.XXX.TLD/oauth2/v1/sysorg/token'; $data = [ 'grant_type' => 'client_credentials', 'client_id' => '<ID>', 'client_secret' => '<SECRET>', 'scope' => '<SCOPE>', ]; $body = http_build_query($data); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_SSLCERT, '/app/keys/eid.crt.pem'); curl_setopt($ch, CURLOPT_SSLKEY, '/app/keys/eid.key.pem'); curl_setopt($ch, CURLOPT_POSTFIELDS, $body); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); // 调试输出 curl_setopt($ch, CURLOPT_VERBOSE, TRUE); $stream = fopen('/tmp/curl_debug.txt', 'w+'); curl_setopt($ch, CURLOPT_STDERR, $stream); $response = curl_exec($ch); curl_close($ch);
排查方向与修复方案
首先明确:CURLOPT_SSLCERT确实对应CLI的--cert,CURLOPT_SSLKEY对应--key,参数映射本身没有问题,问题集中在Docker环境的配置差异上,可从以下几点排查:
1. 证书文件权限检查
Docker容器内的PHP进程(通常是www-data用户)可能没有证书文件的读取权限,导致TLS握手失败。执行以下命令确认并修复权限:
# 进入Docker容器 docker exec -it <容器ID> bash # 检查证书权限 ls -l /app/keys/eid.crt.pem /app/keys/eid.key.pem # 若权限不足,修改权限 chmod 644 /app/keys/eid.crt.pem /app/keys/eid.key.pem chown www-data:www-data /app/keys/eid.crt.pem /app/keys/eid.key.pem
2. 对齐请求头信息
CLI和PHP的User-Agent不一致可能导致API校验失败,将PHP的User-Agent改为与CLI完全一致(可通过curl --version查看CLI的User-Agent):
$headers = [ 'User-Agent: curl/7.74.0', // 替换为你的CLI curl版本对应的UA 'Content-Type: application/x-www-form-urlencoded', 'Accept: */*', ];
3. 指定证书类型与CA根证书
部分环境需要明确指定证书类型,若API使用私有CA颁发的证书,还需指定CA证书路径:
// 明确指定证书类型为PEM curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM'); curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM'); // 若需要验证私有CA,指定CA证书路径(仅生产环境建议) curl_setopt($ch, CURLOPT_CAINFO, '/app/keys/ca.crt.pem'); // 测试阶段可临时关闭证书验证(不建议生产使用) // curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
4. 捕获CURL错误信息
在PHP代码中添加错误捕获,定位具体问题:
$response = curl_exec($ch); if(curl_errno($ch)) { echo 'CURL错误详情:' . curl_error($ch); } curl_close($ch); fclose($stream);
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

