使用stevenmaguire/oauth2-keycloak对接Keycloak22.01获取资源所有者出错
问题:PHP 7.4 对接 Keycloak 22.01 获取资源所有者时JSON解析错误
环境与依赖配置
composer.json 内容:
{ "require": { "stevenmaguire/oauth2-keycloak": "^5.0" } }
报错信息
Uncaught UnexpectedValueException: Failed to parse JSON response: Syntax error in /opt/lampp/htdocs/Demo/vendor/league/oauth2-client/src/Provider/AbstractProvider.php:645 Stack trace: #0 /opt/lampp/htdocs/Demo/vendor/league/oauth2-client/src/Provider/AbstractProvider.php(686): League\OAuth2\Client\Provider\AbstractProvider->parseJson(NULL) #1 /opt/lampp/htdocs/Demo/vendor/stevenmaguire/oauth2-keycloak/src/Provider/Keycloak.php(378): League\OAuth2\Client\Provider\AbstractProvider->parseResponse(Object(GuzzleHttp\Psr7\Response)) #2 /opt/lampp/htdocs/Demo/vendor/league/oauth2-client/src/Provider/AbstractProvider.php(626): Stevenmaguire\OAuth2\Client\Provider\Keycloak->parseResponse(Object(GuzzleHttp\Psr7\Response)) #3 /opt/lampp/htdocs/Demo/vendor/league/oauth2-client/src/Provider/AbstractProvider.php(784): League\OAuth2\Client\Provider\AbstractProvider->getParsedResponse(Object(GuzzleHttp\Psr7\Request)) #4 /opt/lampp/htdocs/Demo/vendor/stevenmaguire/oauth2-keycloak/src/Provider/Keycloak.php(271): League\OAuth2\Client\Provider\AbstractProvider->getParsedResponse(Object(GuzzleHttp\Psr7\Request)) in /opt/lampp/htdocs/Demo/vendor/league/oauth2-client/src/Provider/AbstractProvider.php on line 645
尝试的代码
$provider = new Stevenmaguire\OAuth2\Client\Provider\Keycloak([ 'authServerUrl' => 'http://localhost:8080', 'realm' => 'Demo', 'clientId' => 'email', 'clientSecret' => '{keycloak-client-secret}', 'redirectUri' => 'http://localhost/Demo/', 'encryptionAlgorithm' => 'RS256', // optional ]); if (!isset($_GET['code'])) { // If we don't have an authorization code then get one $authUrl = $provider->getAuthorizationUrl(); $_SESSION['oauth2state'] = $provider->getState(); header('Location: '.$authUrl); exit; } elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) { unset($_SESSION['oauth2state']); exit('Invalid state, make sure HTTP sessions are enabled.'); } else { $token = $provider->getAccessToken('authorization_code', [ 'code' => $_GET['code'] ]); $user = $provider->getResourceOwner($token); print_r($user); try { } catch (Exception $e) { exit('Failed to get access token: '.$e->getMessage()); } try { $user = $provider->getResourceOwner($token); print_r($provider); } catch (Exception $e) { exit('Failed to get resource owner: '.$e->getMessage()); } echo $token->getToken(); }
排查与解决步骤
- 检查用户信息端点响应:报错显示解析NULL内容,说明请求Keycloak的
/realms/Demo/protocol/openid-connect/userinfo接口返回空或非JSON。手动用调试工具带上获取到的AccessToken调用该接口,确认返回格式是否为标准JSON。 - 验证Keycloak客户端权限:在Keycloak后台的Demo Realm中,进入
email客户端的Client Scopes标签页,确保添加了profile、email等必要的OpenID Connect scope并设为默认勾选;同时在Settings标签页确认客户端Access Type与代码配置匹配(confidential需填clientSecret,public无需clientSecret)。 - 修正代码结构:原代码在try块外提前调用
getResourceOwner会直接抛出异常无法被捕获,调整为将所有认证逻辑放入try块:session_start(); // 确保开启会话 try { $provider = new Stevenmaguire\OAuth2\Client\Provider\Keycloak([ 'authServerUrl' => 'http://localhost:8080', 'realm' => 'Demo', 'clientId' => 'email', 'clientSecret' => '{your-real-client-secret}', 'redirectUri' => 'http://localhost/Demo/', ]); if (!isset($_GET['code'])) { $authUrl = $provider->getAuthorizationUrl(); $_SESSION['oauth2state'] = $provider->getState(); header('Location: ' . $authUrl); exit; } elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) { unset($_SESSION['oauth2state']); exit('无效的state,请确保HTTP会话已启用。'); } $token = $provider->getAccessToken('authorization_code', [ 'code' => $_GET['code'] ]); $user = $provider->getResourceOwner($token); print_r($user->toArray()); // 输出用户信息数组 echo '<br>Access Token: ' . $token->getToken(); } catch (Exception $e) { exit('认证失败: ' . $e->getMessage()); } - 升级客户端库版本:stevenmaguire/oauth2-keycloak ^5.0 对Keycloak 22的适配可能存在问题,尝试升级到最新稳定版,执行
composer require stevenmaguire/oauth2-keycloak:^7.0更新依赖。 - 调试请求细节:添加Guzzle调试选项,查看请求和响应的详细内容定位问题:
$provider = new Stevenmaguire\OAuth2\Client\Provider\Keycloak([ // 原有配置 'guzzleOptions' => [ 'debug' => fopen('php://stdout', 'w'), ], ]);
内容的提问来源于stack exchange,提问作者Abhishek Poddar
相关产品推荐
相关产品推荐

