本文目录导读:

我来详细介绍 PHP 中实现授权码模式(Authorization Code)的方法,这是 OAuth 2.0 中最常见的授权方式。
基础概念
授权码模式流程:
用户 → 授权服务器(登录授权)→ 返回授权码 → 客户端 → 令牌服务器(换取Token)→ 返回访问令牌
完整实现示例
1 授权服务器端(Authorization Server)
<?php
// auth_server.php - 授权服务器
class AuthorizationServer {
private $db;
private $clientId = 'your_client_id';
private $clientSecret = 'your_client_secret';
private $redirectUri = 'http://localhost/callback.php';
public function __construct() {
// 连接数据库存储授权码和令牌
$this->db = new PDO('mysql:host=localhost;dbname=oauth', 'root', 'password');
}
// 1. 处理授权请求
public function authorize() {
// 验证客户端参数
$clientId = $_GET['client_id'] ?? '';
$redirectUri = $_GET['redirect_uri'] ?? '';
$responseType = $_GET['response_type'] ?? '';
$state = $_GET['state'] ?? '';
$scope = $_GET['scope'] ?? 'basic';
// 验证客户端
if ($clientId !== $this->clientId) {
$this->sendError('invalid_client');
}
// 验证回调地址
if ($redirectUri !== $this->redirectUri) {
$this->sendError('invalid_redirect_uri');
}
// 检查用户是否已登录
if (!$this->checkUserLoggedIn()) {
// 跳转到登录页面
$this->redirectToLogin($clientId, $redirectUri, $state, $scope);
}
// 生成授权码
$authCode = $this->generateAuthorizationCode();
$this->saveAuthorizationCode($authCode, $clientId, $_SESSION['user_id'], $scope);
// 重定向回客户端应用
$params = [
'code' => $authCode,
'state' => $state
];
$redirectUrl = $redirectUri . '?' . http_build_query($params);
header('Location: ' . $redirectUrl);
exit;
}
// 2. 处理令牌请求
public function token() {
$code = $_POST['code'] ?? '';
$clientId = $_POST['client_id'] ?? '';
$clientSecret = $_POST['client_secret'] ?? '';
$grantType = $_POST['grant_type'] ?? '';
// 验证客户端身份
if (!$this->validateClient($clientId, $clientSecret)) {
$this->sendError('invalid_client');
}
// 验证授权码
$authData = $this->getAuthorizationCode($code);
if (!$authData || $authData['expires'] < time()) {
$this->sendError('invalid_grant');
}
// 生成访问令牌和刷新令牌
$accessToken = $this->generateAccessToken();
$refreshToken = $this->generateRefreshToken();
$expiresIn = 3600; // 1小时
// 保存令牌
$this->saveTokens([
'access_token' => $accessToken,
'refresh_token' => $refreshToken,
'client_id' => $clientId,
'user_id' => $authData['user_id'],
'expires' => time() + $expiresIn
]);
// 删除已使用的授权码
$this->deleteAuthorizationCode($code);
// 返回令牌
header('Content-Type: application/json');
echo json_encode([
'access_token' => $accessToken,
'token_type' => 'Bearer',
'expires_in' => $expiresIn,
'refresh_token' => $refreshToken,
'scope' => $authData['scope']
]);
}
private function generateAuthorizationCode() {
return bin2hex(random_bytes(32));
}
private function generateAccessToken() {
return bin2hex(random_bytes(64));
}
private function generateRefreshToken() {
return bin2hex(random_bytes(64));
}
private function saveAuthorizationCode($code, $clientId, $userId, $scope) {
$stmt = $this->db->prepare(
"INSERT INTO auth_codes (code, client_id, user_id, scope, expires)
VALUES (?, ?, ?, ?, ?)"
);
$stmt->execute([$code, $clientId, $userId, $scope, time() + 600]);
}
private function validateClient($clientId, $clientSecret) {
return $clientId === $this->clientId &&
$clientSecret === $this->clientSecret;
}
private function sendError($error) {
header('Content-Type: application/json');
echo json_encode(['error' => $error]);
exit;
}
}
2 客户端应用(Client Application)
<?php
// client.php - 客户端应用
class OAuthClient {
private $clientId = 'your_client_id';
private $clientSecret = 'your_client_secret';
private $authorizationUrl = 'http://localhost/auth_server.php/authorize';
private $tokenUrl = 'http://localhost/auth_server.php/token';
private $redirectUri = 'http://localhost/callback.php';
private $scope = 'read write';
// 1. 发起授权请求
public function startAuthorization() {
$params = [
'client_id' => $this->clientId,
'redirect_uri' => $this->redirectUri,
'response_type' => 'code',
'scope' => $this->scope,
'state' => bin2hex(random_bytes(16))
];
// 保存state到session用于后续验证
session_start();
$_SESSION['oauth_state'] = $params['state'];
$authUrl = $this->authorizationUrl . '?' . http_build_query($params);
header('Location: ' . $authUrl);
exit;
}
// 2. 处理回调
public function handleCallback() {
session_start();
if (!isset($_GET['code'])) {
$this->handleError('Authorization failed');
}
// 验证state参数
if ($_GET['state'] !== $_SESSION['oauth_state']) {
$this->handleError('Invalid state parameter');
}
$authorizationCode = $_GET['code'];
// 3. 用授权码换取令牌
return $this->exchangeCodeForToken($authorizationCode);
}
// 3. 用授权码换取访问令牌
private function exchangeCodeForToken($code) {
$postData = [
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => $this->redirectUri,
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret
];
$ch = curl_init($this->tokenUrl);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/x-www-form-urlencoded'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
$tokenData = json_decode($response, true);
// 保存令牌到session或数据库
session_start();
$_SESSION['access_token'] = $tokenData['access_token'];
$_SESSION['refresh_token'] = $tokenData['refresh_token'];
$_SESSION['token_expires'] = time() + $tokenData['expires_in'];
return $tokenData;
} else {
$this->handleError('Token exchange failed');
}
}
// 4. 获取用户信息(使用访问令牌)
public function fetchUserProfile() {
session_start();
if (empty($_SESSION['access_token'])) {
$this->handleError('No access token available');
}
$ch = curl_init('http://localhost/api/user/profile');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $_SESSION['access_token'],
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 401) {
// 令牌过期,尝试刷新
$this->refreshAndRetry();
}
return json_decode($response, true);
}
// 5. 刷新令牌
public function refreshToken() {
session_start();
$postData = [
'grant_type' => 'refresh_token',
'refresh_token' => $_SESSION['refresh_token'],
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret
];
$ch = curl_init($this->tokenUrl);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$tokenData = json_decode($response, true);
if (isset($tokenData['access_token'])) {
$_SESSION['access_token'] = $tokenData['access_token'];
$_SESSION['refresh_token'] = $tokenData['refresh_token'];
$_SESSION['token_expires'] = time() + $tokenData['expires_in'];
}
return $tokenData;
}
private function handleError($message) {
header('Content-Type: application/json');
echo json_encode(['error' => $message]);
exit;
}
}
3 回调处理页面
<?php
// callback.php - 回调处理
require_once 'client.php';
$oauthClient = new OAuthClient();
$result = $oauthClient->handleCallback();
// 成功获取令牌
header('Content-Type: application/json');
echo json_encode([
'success' => true,
'message' => 'Authorization successful',
'token_type' => $result['token_type'],
'expires_in' => $result['expires_in']
]);
?>
4 数据库表结构
-- 存储授权码
CREATE TABLE auth_codes (
id INT AUTO_INCREMENT PRIMARY KEY,
code VARCHAR(64) NOT NULL UNIQUE,
client_id VARCHAR(50) NOT NULL,
user_id INT NOT NULL,
scope VARCHAR(255) DEFAULT 'basic',
expires INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 存储访问令牌
CREATE TABLE access_tokens (
id INT AUTO_INCREMENT PRIMARY KEY,
access_token VARCHAR(128) NOT NULL UNIQUE,
refresh_token VARCHAR(128) NOT NULL UNIQUE,
client_id VARCHAR(50) NOT NULL,
user_id INT NOT NULL,
expires INT NOT NULL,
scope VARCHAR(255) DEFAULT 'basic',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 存储客户端应用
CREATE TABLE oauth_clients (
client_id VARCHAR(50) PRIMARY KEY,
client_secret VARCHAR(100) NOT NULL,
redirect_uri VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
使用示例
<?php
// 使用流程示例
// 页面1: 开始授权
if (isset($_GET['login'])) {
$client = new OAuthClient();
$client->startAuthorization();
}
// 页面2: 回调处理
if (isset($_GET['callback'])) {
$client = new OAuthClient();
$result = $client->handleCallback();
// 获取用户信息
$profile = $client->fetchUserProfile();
echo json_encode($profile);
}
?>
最佳实践建议
-
安全性
- 使用 HTTPS 传输
- 验证所有输入参数
- 设置令牌过期时间
- 使用强随机数生成器
-
错误处理
try { $client = new OAuthClient(); $result = $client->handleCallback(); } catch (Exception $e) { error_log($e->getMessage()); // 记录错误 } -
令牌管理
- 定期清理过期令牌
- 支持令牌撤销
- 令牌加密存储
这就是 PHP 授权码模式的完整实现,关键点是保证整个流程的安全性,并正确处理各种错误情况。
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播