OAuth 2.0
一、OAuth2.0 是什么
OAuth 2.0 是一种开放标准的授权协议,用于在不暴露账号密码的情况下,让用户授权第三方应用访问其受保护的资源。
它主要解决的问题是:如何安全地委托访问权限。
核心概念:
| 角色 | 说明 |
|---|---|
| 资源所有者(Resource Owner) | 用户或数据所有者 |
| 客户端(Client) | 第三方应用,需要访问资源 |
| 授权服务器(Authorization Server) | 颁发令牌的服务器,负责验证用户身份并授权 |
| 资源服务器(Resource Server) | 存储用户受保护资源的服务器,验证令牌后提供数据 |
二、 常用场景
第三方登录(Social Login)
用户使用微信、GitHub、Google 登录你的站点,后端获取 Access Token 调用接口获取用户信息。开放 API / 平台化
向合作伙伴提供 API,使用 OAuth 2.0 控制访问权限和作用域。微服务内部接口调用
服务 A 调用服务 B 的接口,通过 Client Credentials 模式获取 Token,避免硬编码密码。企业内部 SSO(单点登录)
多系统统一认证,用户在登录一次后,访问其他系统不需要重复登录。前后端分离应用(SPA / Mobile App)
前端获取授权码,换取 Access Token,安全访问 API,密码不在前端传递。
三、OAuth 2.0 主要流程
以 授权码模式(Authorization Code Grant) 为例:
请求授权:客户端引导用户跳转到授权服务器,传入
client_id、redirect_uri、scope。用户同意:用户在授权服务器上确认授权。
返回授权码:授权服务器重定向回客户端,并携带
code。交换 Access Token:客户端用
code+client_secret向授权服务器请求 Access Token。访问资源:客户端使用 Access Token 调用资源服务器 API。
刷新 Token(可选):Access Token 到期后使用 Refresh Token 获取新的 Access Token。
流程图示意:
[用户] → [客户端] → [授权服务器] → [用户同意] → [客户端] → [授权服务器] → Access Token → [资源服务器] → 数据
四、PHP 实现示例
4.1 安装依赖
composer require league/oauth2-server
4.2 创建授权服务器
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Grant\AuthCodeGrant;
use League\OAuth2\Server\CryptKey;
// 数据存储实现可以使用数据库或内存
$privateKey = new CryptKey('file://path/to/private.key');
$encryptionKey = 'base64:'.base64_encode(random_bytes(32));
$server = new AuthorizationServer(
$clientRepository, // 客户端存储
$accessTokenRepository, // Access Token 存储
$scopeRepository, // Scope 存储
$privateKey,
$encryptionKey
);
// 授权码模式
$grant = new AuthCodeGrant(
$authCodeRepository,
$refreshTokenRepository,
new \DateInterval('PT10M') // 授权码有效期
);
$grant->setRefreshTokenTTL(new \DateInterval('P1M'));
$server->enableGrantType($grant, new \DateInterval('PT1H')); // AccessToken 有效期 1 小时
4.2 发放授权码
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response;
$request = \Zend\Diactoros\ServerRequestFactory::fromGlobals();
$response = new Response();
try {
$authResponse = $server->respondToAccessTokenRequest($request, $response);
echo $authResponse->getBody();
} catch (\League\OAuth2\Server\Exception\OAuthServerException $e) {
echo $e->getMessage();
}
4.3 验证 Access Token
use League\OAuth2\Server\ResourceServer;
use League\OAuth2\Server\Middleware\ResourceServerMiddleware;
// ResourceServer 初始化
$resourceServer = new ResourceServer(
$accessTokenRepository,
new CryptKey('file://path/to/public.key')
);
// 在中间件里验证请求
$middleware = new ResourceServerMiddleware($resourceServer);
通过中间件拦截请求,如果 Access Token 合法,就允许访问 API,否则返回 401
4.4 客户端调用示例(获取用户信息)
$token = 'eyJ0eXAiOiJKV1QiLCJhbGciOi...';
$response = http_client()->get('https://api.example.com/user', [
'headers' => [
'Authorization' => 'Bearer ' . $token
]
]);
$data = json_decode((string)$response->getBody(), true);
print_r($data);