本文目录导读:

我来详细讲解PHP项目中OAuth2.0授权流程的完整实现。
使用OAuth2.0客户端库
推荐使用社区成熟的库,如 league/oauth2-client 或 phpseclib/oauth。
安装依赖
composer require league/oauth2-client
完整授权流程实现
1 配置OAuth2服务提供商
config/oauth.php
<?php
return [
'github' => [
'clientId' => 'your-client-id',
'clientSecret' => 'your-client-secret',
'redirectUri' => 'https://your-domain/callback/github',
'urlAuthorize' => 'https://github.com/login/oauth/authorize',
'urlAccessToken' => 'https://github.com/login/oauth/access_token',
'urlResourceOwnerDetails' => 'https://api.github.com/user',
'scopes' => ['read:user', 'user:email']
],
'google' => [
'clientId' => 'your-google-client-id',
'clientSecret' => 'your-google-client-secret',
'redirectUri' => 'https://your-domain/callback/google',
'urlAuthorize' => 'https://accounts.google.com/o/oauth2/v2/auth',
'urlAccessToken' => 'https://oauth2.googleapis.com/token',
'urlResourceOwnerDetails' => 'https://www.googleapis.com/oauth2/v2/userinfo',
'scopes' => ['https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile']
]
];
2 OAuth服务类
services/OAuthService.php
<?php
namespace App\Services;
use League\OAuth2\Client\Provider\AbstractProvider;
use League\OAuth2\Client\Token\AccessToken;
use App\Models\User;
use App\Repositories\UserRepository;
use GuzzleHttp\Client as HttpClient;
class OAuthService
{
private $provider;
private $userRepository;
private $config;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
$this->config = require __DIR__ . '/../config/oauth.php';
}
/**
* 获取授权URL
*/
public function getAuthorizationUrl(string $providerName): string
{
$provider = $this->getProvider($providerName);
// 生成state参数防止CSRF
$state = bin2hex(random_bytes(32));
$_SESSION['oauth_state'] = $state;
$_SESSION['oauth_provider'] = $providerName;
$authorizationUrl = $provider->getAuthorizationUrl([
'state' => $state,
'scope' => $this->config[$providerName]['scopes']
]);
return $authorizationUrl;
}
/**
* 处理回调,获取access token和用户信息
*/
public function handleCallback(string $providerName, string $code, string $state): array
{
// 验证state
if (!isset($_SESSION['oauth_state']) || $state !== $_SESSION['oauth_state']) {
throw new \Exception('Invalid state parameter');
}
$provider = $this->getProvider($providerName);
try {
// 用授权码换取access token
$accessToken = $provider->getAccessToken('authorization_code', [
'code' => $code
]);
// 使用access token获取用户信息
$resourceOwner = $provider->getResourceOwner($accessToken);
$userData = $resourceOwner->toArray();
// 查找或创建用户
$user = $this->findOrCreateUser($providerName, $userData, $accessToken);
// 清除session中的state
unset($_SESSION['oauth_state']);
unset($_SESSION['oauth_provider']);
return [
'user' => $user,
'access_token' => $accessToken->getToken(),
'refresh_token' => $accessToken->getRefreshToken(),
'expires' => $accessToken->getExpires()
];
} catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) {
throw new \Exception('OAuth Error: ' . $e->getMessage());
}
}
/**
* 刷新access token
*/
public function refreshToken(string $providerName, string $refreshToken): AccessToken
{
$provider = $this->getProvider($providerName);
$newAccessToken = $provider->getAccessToken('refresh_token', [
'refresh_token' => $refreshToken
]);
return $newAccessToken;
}
/**
* 查找或创建用户
*/
private function findOrCreateUser(string $provider, array $userData, AccessToken $accessToken): User
{
$email = $userData['email'] ?? $userData['login'] . '@github.oauth';
// 根据提供商ID查找已有用户
$user = $this->userRepository->findByOAuthProvider($provider, $userData['id']);
if (!$user) {
// 创建新用户
$user = $this->userRepository->create([
'name' => $userData['name'] ?? $userData['login'],
'email' => $email,
'password' => password_hash(bin2hex(random_bytes(16)), PASSWORD_DEFAULT),
'email_verified_at' => now(),
'oauth_provider' => $provider,
'oauth_id' => (string)$userData['id'],
'avatar_url' => $userData['avatar_url'] ?? null
]);
}
// 更新access token信息
$this->userRepository->updateOAuthToken($user->id, [
'access_token' => $accessToken->getToken(),
'refresh_token' => $accessToken->getRefreshToken(),
'token_expires_at' => date('Y-m-d H:i:s', $accessToken->getExpires())
]);
return $user;
}
/**
* 获取OAuth2提供商实例
*/
private function getProvider(string $name): AbstractProvider
{
$config = $this->config[$name];
switch ($name) {
case 'github':
return new \League\OAuth2\Client\Provider\Github([
'clientId' => $config['clientId'],
'clientSecret' => $config['clientSecret'],
'redirectUri' => $config['redirectUri']
]);
case 'google':
return new \League\OAuth2\Client\Provider\Google([
'clientId' => $config['clientId'],
'clientSecret' => $config['clientSecret'],
'redirectUri' => $config['redirectUri']
]);
default:
throw new \InvalidArgumentException("Unsupported provider: {$name}");
}
}
}
3 控制器实现
controllers/AuthController.php
<?php
namespace App\Controllers;
use App\Services\OAuthService;
use App\Repositories\UserRepository;
class AuthController
{
private $oauthService;
public function __construct()
{
$this->oauthService = new OAuthService(new UserRepository());
}
/**
* 跳转到OAuth提供商授权页面
*/
public function redirect(string $provider)
{
$authorizationUrl = $this->oauthService->getAuthorizationUrl($provider);
header('Location: ' . $authorizationUrl);
exit;
}
/**
* 处理OAuth回调
*/
public function callback(string $provider)
{
try {
$code = $_GET['code'];
$state = $_GET['state'];
$result = $this->oauthService->handleCallback($provider, $code, $state);
// 创建会话
$_SESSION['user_id'] = $result['user']->id;
$_SESSION['access_token'] = $result['access_token'];
// 返回JSON或重定向
header('Content-Type: application/json');
echo json_encode([
'success' => true,
'message' => '登录成功',
'user' => $result['user']
]);
} catch (\Exception $e) {
http_response_code(400);
echo json_encode([
'success' => false,
'message' => $e->getMessage()
]);
}
}
/**
* 用户登出
*/
public function logout()
{
session_destroy();
header('Location: /login');
exit;
}
}
4 路由配置
routes/web.php
<?php
// 授权入口
Route::get('/auth/{provider}', 'AuthController@redirect');
// 回调处理
Route::get('/callback/{provider}', 'AuthController@callback');
// 登录页面(可选)
Route::get('/login', function() {
echo '<a href="/auth/github">使用GitHub登录</a><br>';
echo '<a href="/auth/google">使用Google登录</a>';
});
// 登出
Route::post('/logout', 'AuthController@logout');
自定义OAuth提供商
如果需要支持自定义API的OAuth2:
<?php
namespace App\OAuth;
use League\OAuth2\Client\Provider\AbstractProvider;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use Psr\Http\Message\ResponseInterface;
class CustomProvider extends AbstractProvider
{
private $apiDomain;
public function __construct(array $options = [], array $collaborators = [])
{
parent::__construct($options, $collaborators);
$this->apiDomain = $options['apiDomain'] ?? 'https://api.example.com';
}
public function getBaseAuthorizationUrl(): string
{
return $this->apiDomain . '/oauth/authorize';
}
public function getBaseAccessTokenUrl(array $params): string
{
return $this->apiDomain . '/oauth/token';
}
public function getResourceOwnerDetailsUrl(AccessToken $token): string
{
return $this->apiDomain . '/api/user';
}
protected function getDefaultScopes(): array
{
return ['basic'];
}
protected function checkResponse(ResponseInterface $response, $data): void
{
if ($response->getStatusCode() >= 400) {
throw new IdentityProviderException(
$data['error'] ?? $response->getReasonPhrase(),
$response->getStatusCode(),
$response
);
}
}
protected function createResourceOwner(array $response, AccessToken $token): CustomResourceOwner
{
return new CustomResourceOwner($response);
}
}
安全注意事项
<?php
// 1. 始终使用HTTPS
if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') {
header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
exit;
}
// 2. State参数验证
// 在回调处理中验证state
// 3. 存储access token安全
// 使用加密存储
$encryptedToken = openssl_encrypt(
$token,
'aes-256-cbc',
$_ENV['TOKEN_ENCRYPTION_KEY'],
0,
$iv
);
// 4. 定期刷新token
if ($accessToken->hasExpired()) {
$newToken = $oauthService->refreshToken($provider, $refreshToken);
}
// 5. 限制重定向URI
// 在provider配置中只允许特定的回调URL
完整示例流程图
用户点击"使用GitHub登录"
↓
跳转到GitHub授权页面
↓
用户确认授权
↓
GitHub重定向回你的回调URL
↓
用授权码换取access token
↓
使用access token获取用户信息
↓
查找或创建本地用户
↓
创建用户会话
↓
返回成功响应或重定向到首页
这个实现涵盖了OAuth2.0授权码流程的完整步骤,包括state验证、token管理、用户信息同步等关键部分。
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播