在實現 QQ 登入回調時,使用 file_get_contents 無法正確獲取資料,改用 cURL 卻遇到 SSL 憑證驗證失敗。本文記錄問題排查過程與解決方案,供遇到相同問題的開發者參考。
file_get_contents 請求 HTTPS 介面無回應或資料不全SSL certificate problem: unable to get local issuer certificatecURL 預設啟用 SSL 對等驗證(CURLOPT_SSL_VERIFYPEER = true),需要本機有有效的 CA 憑證包(cacert.pem)才能驗證伺服器憑證鏈。若 PHP 環境未配置或憑證過期,驗證會失敗。
暫時關閉 SSL 驗證:
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://graph.qq.com/user/get_user_info?...');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// 關閉 SSL 對等驗證與主機名驗證(僅限測試環境)
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo 'cURL 錯誤: ' . curl_error($ch);
} else {
echo $response;
}
curl_close($ch);
?>
/etc/ssl/certs/cacert.pem 或 /usr/local/php/cacert.pemphp.ini:; 指定 CA 憑證檔案路徑
curl.cainfo = "/usr/local/php/cacert.pem"
openssl.cafile = "/usr/local/php/cacert.pem"
curl_setopt($ch, CURLOPT_CAINFO, '/path/to/cacert.pem');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
網上有建議設定 CURLOPT_HTTPHEADER => ['Expect:'] 解決,實測對憑證驗證錯誤無效,該選項主要針對 100 Continue 行為。
<?php
function getQQUserInfo($accessToken, $openId, $appId)
{
$url = 'https://graph.qq.com/user/get_user_info';
$params = [
'access_token' => $accessToken,
'oauth_consumer_key' => $appId,
'openid' => $openId,
'format' => 'json'
];
$queryString = http_build_query($params);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url . '?' . $queryString);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); // 生產環境必須開啟
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
// 若未在 php.ini 設定全域 cacert,可在此指定
// curl_setopt($ch, CURLOPT_CAINFO, '/usr/local/php/cacert.pem');
$response = curl_exec($ch);
$errNo = curl_errno($ch);
$errMsg = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($errNo) {
error_log("QQ用戶資訊獲取失敗: [$errNo] $errMsg, HTTP: $httpCode");
return false;
}
$data = json_decode($response, true);
if (isset($data['ret']) && $data['ret'] != 0) {
error_log("QQ API 返回錯誤: " . json_encode($data));
return false;
}
return $data;
}
?>
cacert.pem(建議每季度),避免根憑證過期導致驗證失敗SSL 憑證驗證是 HTTPS 安全的基石,正確配置而非單純關閉,才是專業開發者的選擇。