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

PHP Curl调用Keycloak接口报错但实际请求成功的解决咨询

PHP Curl连接Keycloak获取令牌时提示连接失败,但实际请求已成功

我正在为应用实现OAuth2授权,用Keycloak做用户认证。已经成功从Keycloak拿到code,之后在PHP模块里用Curl把code提交到Keycloak的REST API获取用户令牌,但收到“无法连接到服务器”的错误。不过Firefox开发者工具和Keycloak日志都显示请求成功并返回了access_token。

相关PHP代码

$ch = curl_init("http://localhost:8080/realms/openemr-local/protocol/openid-connect/token");
            
$cparams = ['code' => $kc_code, 'grant_type'=>'authorization_code', 'redirect_uri'=>'http://localhost:8300/interface/login/login.php?kc_response=true', 'client_id'=>'openemr'];
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $cparams);
         
$ch_response = curl_exec($ch);
if( curl_error($ch)) {
    echo '<br>curl error: - ' . curl_error($ch);
    echo '    and returned: ' . $ch_response;
}
else {
    echo '<br>got  token response: ' . $ch_response;
            
    $json_text = json_decode($ch_response);
    echo $json_text->contents . '<br>';
}

Curl错误信息

Failed to connect to localhost port 8080 after 0 ms: Couldn't connect to server and returned:

我的问题:

  1. 如何让PHP Curl函数识别请求无错误?
  2. 如何让Curl返回正确的数据?

问题原因及解决方案

1. 解决Curl误报连接失败的问题

这个问题大概率是PHP运行环境的localhost解析问题:

  • 如果PHP运行在Docker容器内,localhost指向的是容器自身,而非宿主机上的Keycloak服务,此时可以替换localhost为宿主机的实际IP,或是使用host.docker.internal(Docker Desktop环境支持)。
  • 检查PHP所在环境的hosts配置,确保localhost正确映射到127.0.0.1,同时确认Keycloak监听的是该IP(不要仅监听容器内部IP)。

也可以添加调试参数排查连接细节:

curl_setopt($ch, CURLOPT_VERBOSE, true);
$verbose = fopen('php://temp', 'w+');
curl_setopt($ch, CURLOPT_STDERR, $verbose);
// 执行后输出调试日志
rewind($verbose);
$verboseLog = stream_get_contents($verbose);
echo "调试日志: " . $verboseLog;

2. 让Curl返回正确数据的调整

除连接问题外,代码还有几个需要修正的点:

  • Keycloak的token接口要求POST数据为form-urlencoded格式,直接传数组会被Curl编码为multipart/form-data,不符合接口要求,需转为urlencoded字符串:
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($cparams));
    
  • 添加请求头明确数据格式:
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/x-www-form-urlencoded'
    ]);
    
  • 设置CURLOPT_RETURNTRANSFER为true,让Curl返回响应内容而非直接输出:
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    
  • 设置CURLOPT_FAILONERROR为false,确保即使返回非200状态码也能拿到响应内容:
    curl_setopt($ch, CURLOPT_FAILONERROR, false);
    
  • 原代码中$json_text->contents是错误的,Keycloak返回的令牌字段为access_token,需修正字段名。

修正后的完整代码:

$ch = curl_init("http://192.168.x.x:8080/realms/openemr-local/protocol/openid-connect/token");
            
$cparams = [
    'code' => $kc_code,
    'grant_type' => 'authorization_code',
    'redirect_uri' => 'http://localhost:8300/interface/login/login.php?kc_response=true',
    'client_id' => 'openemr'
];

curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($cparams));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/x-www-form-urlencoded'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FAILONERROR, false);
         
$ch_response = curl_exec($ch);
if( curl_error($ch)) {
    echo '<br>curl error: - ' . curl_error($ch);
    echo '    and returned: ' . $ch_response;
}
else {
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    echo '<br>HTTP状态码: ' . $httpCode . '<br>';
    echo '<br>拿到令牌响应: ' . $ch_response;
            
    $json_text = json_decode($ch_response);
    if ($json_text) {
        echo 'Access Token: ' . $json_text->access_token . '<br>';
    } else {
        echo '解析JSON失败<br>';
    }
}

curl_close($ch);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 03:49:55