本文目录导读:

我来为您详细讲解如何在PHP项目中实现OpenID Connect认证。
OpenID Connect基础概念
OpenID Connect (OIDC) 是建立在OAuth 2.0之上的身份认证层,主要包含:
- ID Token:JWT格式的身份令牌
- Access Token:访问令牌
- Refresh Token:刷新令牌
- UserInfo Endpoint:用户信息端点
使用官方库实现(推荐)
使用 league/oauth2-client 库
composer require league/oauth2-client
使用 jumbojett/openid-connect-php 库
composer require jumbojett/openid-connect-php
完整实现示例
1 基础配置
<?php
// config.php
class OIDCConfig {
// Provider配置
const PROVIDER_URL = 'https://your-provider.com';
const CLIENT_ID = 'your-client-id';
const CLIENT_SECRET = 'your-client-secret';
const REDIRECT_URI = 'https://your-app.com/callback.php';
// 请求的权限范围
const SCOPES = ['openid', 'profile', 'email', 'offline_access'];
}
?>
2 使用jumbojett库的完整实现
<?php
// login.php - 发起认证请求
require_once 'vendor/autoload.php';
require_once 'config.php';
use Jumbojett\OpenIDConnectClient;
session_start();
// 创建OIDC客户端
$oidc = new OpenIDConnectClient(
OIDCConfig::PROVIDER_URL,
OIDCConfig::CLIENT_ID,
OIDCConfig::CLIENT_SECRET
);
// 设置重定向URI
$oidc->setRedirectURL(OIDCConfig::REDIRECT_URI);
// 设置请求的作用域
$oidc->addScope(OIDCConfig::SCOPES);
// 添加认证参数(可选)
$oidc->addAuthParam(['prompt' => 'login']);
// 保存状态用于防CSRF
$_SESSION['oidc_state'] = $oidc->getState();
// 发起认证请求
$oidc->authenticate();
?>
<?php
// callback.php - 处理认证回调
require_once 'vendor/autoload.php';
require_once 'config.php';
use Jumbojett\OpenIDConnectClient;
session_start();
// 验证state防止CSRF
if (isset($_GET['state']) && $_SESSION['oidc_state'] !== $_GET['state']) {
die('State mismatch, possible CSRF attack');
}
try {
$oidc = new OpenIDConnectClient(
OIDCConfig::PROVIDER_URL,
OIDCConfig::CLIENT_ID,
OIDCConfig::CLIENT_SECRET
);
$oidc->setRedirectURL(OIDCConfig::REDIRECT_URI);
// 处理认证回调
$oidc->authenticate();
// 获取用户信息
$userInfo = $oidc->requestUserInfo();
// 获取ID Token
$idToken = $oidc->getIdToken();
// 获取Access Token
$accessToken = $oidc->getAccessToken();
// 获取Refresh Token(如果配置了offline_access)
$refreshToken = $oidc->getRefreshToken();
// 保存用户会话
$_SESSION['user'] = [
'sub' => $userInfo->sub,
'email' => $userInfo->email ?? '',
'name' => $userInfo->name ?? '',
'given_name' => $userInfo->given_name ?? '',
'family_name' => $userInfo->family_name ?? '',
'picture' => $userInfo->picture ?? '',
'access_token' => $accessToken,
'id_token' => $idToken,
'refresh_token' => $refreshToken,
'login_time' => time()
];
// 重定向到受保护的页面
header('Location: /dashboard.php');
exit;
} catch (Exception $e) {
error_log('OIDC authentication error: ' . $e->getMessage());
header('Location: /login-error.php?error=' . urlencode($e->getMessage()));
exit;
}
?>
3 使用league/oauth2-client实现
<?php
// oidc_league.php - 使用league/oauth2-client
require_once 'vendor/autoload.php';
require_once 'config.php';
use League\OAuth2\Client\Provider\GenericProvider;
session_start();
class OIDCOAuth2Client {
private $provider;
public function __construct() {
$this->provider = new GenericProvider([
'clientId' => OIDCConfig::CLIENT_ID,
'clientSecret' => OIDCConfig::CLIENT_SECRET,
'redirectUri' => OIDCConfig::REDIRECT_URI,
'urlAuthorize' => OIDCConfig::PROVIDER_URL . '/authorize',
'urlAccessToken' => OIDCConfig::PROVIDER_URL . '/token',
'urlResourceOwnerDetails' => OIDCConfig::PROVIDER_URL . '/userinfo',
'scopes' => OIDCConfig::SCOPES
]);
}
public function authenticate() {
// 检查授权回调
if (!isset($_GET['code'])) {
// 获取授权URL
$authorizationUrl = $this->provider->getAuthorizationUrl();
$_SESSION['oauth2state'] = $this->provider->getState();
header('Location: ' . $authorizationUrl);
exit;
}
// 验证state
if (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) {
unset($_SESSION['oauth2state']);
exit('Invalid state');
}
try {
// 获取访问令牌
$accessToken = $this->provider->getAccessToken('authorization_code', [
'code' => $_GET['code']
]);
// 获取用户信息
$resourceOwner = $this->provider->getResourceOwner($accessToken);
$userData = $resourceOwner->toArray();
// 保存会话
$_SESSION['user'] = [
'id' => $userData['sub'],
'email' => $userData['email'],
'name' => $userData['name'],
'access_token' => $accessToken->getToken(),
'expires' => $accessToken->getExpires()
];
return true;
} catch (Exception $e) {
error_log('Authentication failed: ' . $e->getMessage());
return false;
}
}
}
// 使用示例
$client = new OIDCOAuth2Client();
if ($client->authenticate()) {
header('Location: /dashboard.php');
exit;
} else {
header('Location: /login-error.php');
exit;
}
?>
4 用户会话管理和中间件
<?php
// auth_middleware.php - 认证中间件
class AuthMiddleware {
public static function requireLogin() {
session_start();
if (!self::isLoggedIn()) {
header('Location: /login.php');
exit;
}
// 检查令牌是否过期
if (self::isTokenExpired()) {
self::refreshToken();
}
return $_SESSION['user'];
}
public static function isLoggedIn() {
return isset($_SESSION['user']['id']);
}
public static function isTokenExpired() {
if (!isset($_SESSION['user']['token_expires'])) {
return true;
}
return time() >= $_SESSION['user']['token_expires'];
}
public static function refreshToken() {
// 使用refresh token刷新
require_once 'vendor/autoload.php';
$refreshToken = $_SESSION['user']['refresh_token'] ?? null;
if (!$refreshToken) {
self::logout();
return false;
}
try {
$oidc = new OpenIDConnectClient(
OIDCConfig::PROVIDER_URL,
OIDCConfig::CLIENT_ID,
OIDCConfig::CLIENT_SECRET
);
// 刷新令牌
$newToken = $oidc->refreshToken($refreshToken);
// 更新会话
$_SESSION['user']['access_token'] = $newToken->getAccessToken();
$_SESSION['user']['token_expires'] = time() + $newToken->getExpiresIn();
$_SESSION['user']['refresh_token'] = $newToken->getRefreshToken();
return true;
} catch (Exception $e) {
error_log('Token refresh failed: ' . $e->getMessage());
self::logout();
return false;
}
}
public static function logout() {
session_destroy();
header('Location: /login.php');
exit;
}
}
?>
5 安全最佳实践
<?php
// security.php - 安全配置
class Security {
// 验证ID Token
public static function validateIdToken($idToken) {
$parts = explode('.', $idToken);
if (count($parts) !== 3) {
return false;
}
list($header, $payload, $signature) = $parts;
// 解码payload
$decodedPayload = json_decode(base64_decode(strtr($payload, '-_', '+/')), true);
// 验证过期时间
if ($decodedPayload['exp'] < time()) {
return false;
}
// 验证issuer
if ($decodedPayload['iss'] !== OIDCConfig::PROVIDER_URL) {
return false;
}
// 验证audience
if ($decodedPayload['aud'] !== OIDCConfig::CLIENT_ID) {
return false;
}
return $decodedPayload;
}
// 生成安全的随机状态
public static function generateState() {
return bin2hex(random_bytes(32));
}
// CSRF防护
public static function verifyCSRF($token) {
return hash_equals($_SESSION['csrf_token'] ?? '', $token);
}
}
// 认证成功后的用户授权检查
class Authorization {
public static function checkPermission($user, $requiredRole) {
$userRoles = $user['roles'] ?? [];
return in_array($requiredRole, $userRoles);
}
public static function requireRole($role) {
$user = AuthMiddleware::requireLogin();
if (!self::checkPermission($user, $role)) {
http_response_code(403);
die('Access denied');
}
return $user;
}
}
?>
6 完整的登录页面示例
<!-- login.php -->
<?php
require_once 'config.php';
session_start();
?>
<!DOCTYPE html>
<html>
<head>登录</title>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; margin: 40px auto; max-width: 400px; }
.login-btn { display: block; width: 100%; padding: 15px; background: #4CAF50; color: white; text-decoration: none; text-align: center; border-radius: 5px; }
.error { color: red; margin: 10px 0; }
</style>
</head>
<body>
<h1>用户登录</h1>
<?php if (isset($_GET['error'])): ?>
<div class="error"><?php echo htmlspecialchars($_GET['error']); ?></div>
<?php endif; ?>
<a href="/initiate_oidc.php" class="login-btn">使用OIDC账户登录</a>
</body>
</html>
<?php
// initiate_oidc.php - 发起OIDC认证
require_once 'vendor/autoload.php';
require_once 'config.php';
session_start();
$oidc = new OpenIDConnectClient(
OIDCConfig::PROVIDER_URL,
OIDCConfig::CLIENT_ID,
OIDCConfig::CLIENT_SECRET
);
$oidc->setRedirectURL(OIDCConfig::REDIRECT_URI);
$oidc->addScope(['openid', 'profile', 'email']);
$oidc->authenticate();
?>
配置文件示例
<?php
// config_example.php - 配置示例
return [
'oidc' => [
'provider_url' => 'https://your-identity-provider.com',
'client_id' => 'your-client-id',
'client_secret' => 'your-client-secret',
'redirect_uri' => 'https://your-app.com/callback.php',
'scopes' => ['openid', 'profile', 'email'],
'response_type' => 'code',
'grant_type' => 'authorization_code',
'allow_refresh_tokens' => true
],
'session' => [
'name' => 'oidc_session',
'lifetime' => 3600, // 1小时
'secure' => true,
'http_only' => true,
'same_site' => 'Lax'
],
'cache' => [
'enabled' => true,
'ttl' => 300 // 5分钟缓存JWT
]
];
?>
测试和调试
<?php
// test_oidc.php - 测试脚本
require_once 'vendor/autoload.php';
function testOIDCConfiguration() {
// 测试OIDC配置
echo "测试OIDC配置...\n";
// 1. 检查发现端点
$issuer = OIDCConfig::PROVIDER_URL;
$wellKnownUrl = $issuer . '/.well-known/openid-configuration';
$ch = curl_init($wellKnownUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (!$response) {
echo "❌ 无法访问OIDC发现端点\n";
return false;
}
$config = json_decode($response, true);
if (!isset($config['authorization_endpoint'], $config['token_endpoint'], $config['userinfo_endpoint'])) {
echo "❌ OIDC发现端点配置不完整\n";
return false;
}
echo "✅ OIDC提供者配置正确\n";
return true;
}
testOIDCConfiguration();
?>
这就是一个完整的PHP OpenID Connect认证实现,关键要点:
- 选择合适的库:建议使用
jumbojett/openid-connect-php因为它专门针对OIDC实现 - 安全第一:始终验证state、令牌签名和过期时间
- 会话管理:合理管理access token和refresh token的生命周期
- 错误处理:提供友好的错误提示和日志记录
需要根据您的具体OIDC提供者(如Keycloak、Auth0、Azure AD等)调整配置。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播