OpenID Connect 客户端无需调用IdP的Token验证及PHP实现咨询
方案可行性说明
该方案完全可行。OIDC的ID Token、Access Token本质都是自包含的JWT结构,本地验证是标准的token校验方案,相比调用Keycloak的token内省端点性能高很多,非常适合你提到的受控内网环境场景。
中间件必备信息
- Keycloak对应Realm的RS256签名公钥:Keycloak默认使用非对称加密RS256算法签名JWT,仅需公钥即可完成验签,无需掌握私钥
- 匹配的签发者(iss)字段:格式一般为
http(s)://<你的Keycloak服务地址>/realms/<realm名称>,必须和token内的iss声明完全一致 - 允许的受众(aud)列表:ID Token的aud默认是你的客户端ID,Access Token的aud对应你配置的资源服务器标识,校验时需匹配预期值
- 可选校验规则:比如允许的scope列表、自定义声明校验规则,可根据业务需求补充
PHP实现步骤
1. 安装依赖
推荐使用主流的firebase/php-jwt库处理JWT校验,执行composer命令安装:composer require firebase/php-jwt
2. 获取Keycloak公钥
登录Keycloak管理后台,进入对应Realm,依次点击「Realm settings」→「Keys」标签,找到算法为RS256的条目,点击右侧「Public key」按钮复制公钥内容,给内容补充首尾标识后得到完整PEM格式公钥:
-----BEGIN PUBLIC KEY----- 你复制的公钥内容 -----END PUBLIC KEY-----
3. 验证代码示例
<?php require __DIR__ . '/vendor/autoload.php'; use Firebase\JWT\JWT; use Firebase\JWT\Key; use Firebase\JWT\ExpiredException; use Firebase\JWT\BeforeValidException; use Firebase\JWT\SignatureInvalidException; // 配置项,替换为你自己的实际值 $keycloakPublicKey = <<<EOD -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx... 这里替换成你的公钥内容 -----END PUBLIC KEY----- EOD; $expectedIss = 'https://your-keycloak-domain.com/realms/your-realm'; $allowedAud = ['your-client-id', 'your-resource-server-id']; // 允许的受众列表 function validateKeycloakToken(string $token, string $publicKey, string $expectedIss, array $allowedAud): array|bool { try { $decoded = JWT::decode($token, new Key($publicKey, 'RS256')); // 转成数组方便处理 $decodedArr = (array)$decoded; // 校验签发者 if ($decodedArr['iss'] !== $expectedIss) { return false; } // 校验受众,支持单aud和多aud场景 $aud = $decodedArr['aud'] ?? ''; $audIsValid = false; if (is_string($aud) && in_array($aud, $allowedAud)) { $audIsValid = true; } elseif (is_array($aud)) { foreach ($aud as $item) { if (in_array($item, $allowedAud)) { $audIsValid = true; break; } } } if (!$audIsValid) { return false; } // 如需校验scope可以在这里加逻辑 // $expectedScopes = ['openid', 'profile']; // $tokenScopes = explode(' ', $decodedArr['scope'] ?? ''); // if (!count(array_intersect($expectedScopes, $tokenScopes))) return false; return $decodedArr; } catch (ExpiredException $e) { // token已过期 return false; } catch (BeforeValidException $e) { // token尚未生效 return false; } catch (SignatureInvalidException $e) { // 签名校验失败,token被篡改 return false; } catch (Exception $e) { // 其他错误 return false; } } // 使用示例 $token = '你要验证的id token或者access token'; $tokenInfo = validateKeycloakToken($token, $keycloakPublicKey, $expectedIss, $allowedAud); if ($tokenInfo) { // 验证通过,处理业务逻辑 var_dump($tokenInfo); } else { // 验证失败,返回401 http_response_code(401); exit('Token invalid'); }
注意事项
- 不要使用HS256等对称加密算法签名token,此类算法需要共享密钥,安全性远低于RS256非对称方案
- 受控环境下如果Keycloak有签名密钥轮转操作,同步更新中间件内的公钥即可,轮转频率低,维护成本很低
内容的提问来源于stack exchange,提问作者Rammboss
相关产品推荐
相关产品推荐

