作者:baiyi
链接:https://www.jianshu.com/p/83b0f6d82d6c
來源:简书
OAuth2.0 是关于授权的开放网络标准,它允许用户已第三方应用获取该用户在某一网站的私密资源,而无需提供用户名与密码,目前已在全世界得到广泛应用。
league/oauth2-server 是一个轻量级并且功能强大的符合 OAuth2.0 协议的 PHP 库,使用它可以构建出标准的 OAuth2.0 授权服务器。
本文通过对 PHP 库:league/oauth2-server 进行实践的同时,理解 OAuth2.0 的工作流程与设计思路。
术语
了解 OAuth2.0 与 oauth2-server 的专用术语,对于理解后面内容很有帮助。
OAuth2.0 定义了四个角色
- Client:客户端,第三方应用程序。
- Resource Owner:资源所有者,授权 Client 访问其帐户的用户。
- Authorization server:授权服务器,服务商专用于处理用户授权认证的服务器。
- Resource server:资源服务器,服务商用于存放用户受保护资源的服务器,它可以与授权服务器是同一台服务器,也可以是不同的服务器。
oauth2-server
- Access token:用于访问受保护资源的令牌。
- Authorization code:发放给应用程序的中间令牌,客户端应用使用此令牌交换 access token。
- Scope:授予应用程序的权限范围。
- JWT:Json Web Token 是一种用于安全传输的数据传输格式。
运行流程
安装
推荐使用 Composer 进行安装:
composer require league/oauth2-server
根据授权模式的不同,oauth2-server 提供了不同的 Interface 与 Triat 帮助实现。
本文发布时,版本号为7.3.1。
生成公钥与私钥
公钥与私钥用于签名和验证传输的 JWT,授权服务器使用私钥签名 JWT,资源服务器拥有公钥验证 JWT。
oauth2-server 使用 JWT 传输访问令牌(access token),方便资源服务器获取其中内容,所以需要使用非对称加密。
生成私钥,在终端中运行:
openssl genrsa -out private.key 2048
使用私钥提取私钥:
openssl rsa -in private.key -pubout -out public.key
私钥必须保密于授权服务器中,并将公钥分发给资源服务器。
生成加密密钥
加密密钥用于加密授权码(auth code)与刷新令牌(refesh token),AuthorizationServer(授权服务器启动类)接受两种加密密钥,string
或 defuse/php-encryption
库的对象。
加密授权码(auth code)与刷新令牌(refesh token)只有授权权服务器使用,所以使用对称加密。
生成字符串密钥,在终端中输入:
php -r 'echo base64_encode(random_bytes(32)), PHP_EOL;'
生成对象,在项目根目录的终端中输入:
vendor/bin/generate-defuse-key
将获得的内容,传入 AuthorizationServer:
use \Defuse\Crypto\Key; $server = new AuthorizationServer( $clientRepository, $accessTokenRepository, $scopeRepository, $privateKeyPath, Key::loadFromAsciiSafeString($encryptionKey) //传入加密密钥 );
PHP版本支持
- PHP 7.0
- PHP 7.1
- PHP 7.2
授权模式
OAuth2.0 定义了四种授权模式,以应对不同情况时的授权。
- 授权码模式
- 隐式授权模式
- 密码模式
- 客户端模式
客户端类型
- 保密的:
- 客户端可以安全的存储自己与用户的凭据(例如:有所属的服务器端)
- 公开的:
- 客户端无法安全的存储自己与用户的凭据(例如:运行在浏览器的单页应用)
选用哪种授权模式?
如果客户端是保密的,应使用授权码模式。
如果客户端是公开的,应使用隐式授权模式。
如果用户对于此客户端高度信任(例如:第一方应用程序或操作系统程序),应使用密码模式。
如果客户端是以自己的名义,不与用户产生关系,应使用客户端模式。
预先注册
客户端需要预先在授权服务器进行注册,用以获取 client_id
与 client_secret
,也可以在注册是预先设定好 redirect_uri
,以便于之后可以使用默认的 redirect_uri
。
授权码模式
授权码模式是 OAuth2.0 种功能最完整,流程最严密的一种模式,如果你使用过 Google 或 QQ 登录过第三方应用程序,应该会对这个流程的第一部分很熟悉。
流程
第一部分(用户可见)
用户访问客户端,客户端将用户导向授权服务器时,将以下参数通过 GET query
传入:
response_type
:授权类型,必选项,值固定为:code
client_id
:客户端ID,必选项redirect_uri
:重定向URI,可选项,不填写时默认预先注册的重定向URIscope
:权限范围,可选项,以空格分隔state
:CSRF令牌,可选项,但强烈建议使用,应将该值存储与用户会话中,以便在返回时验证
用户选择是否给予客户端授权
假设用户给予授权,授权服务器将用户导向客户端事先指定的 redirect_uri
,并将以下参数通过 GET query
传入:
code
:授权码(Authorization code)state
:请求中发送的state
,原样返回。客户端将此值与用户会话中的值进行对比,以确保授权码响应的是此客户端而非其他客户端程序
第二部分(用户不可见)
客户端已得到授权,通过 POST
请求向授权服务器获取访问令牌(access token):
grant_type
:授权模式,值固定为:authorization_code
client_id
:客户端IDclient_secret
:客户端 secretredirect_uri
:使用与第一部分请求相同的 URIcode
:第一部分所获的的授权码,要注意URL解码
授权服务器核对授权码与重定向 URI,确认无误后,向客户端响应下列内容:
-
token_type
:令牌类型,值固定为:Bearer
-
expires_in
:访问令牌的存活时间 -
access_token
:访问令牌 -
refresh_token
:刷新令牌,访问令牌过期后,使用刷新令牌重新获取
使用 oauth2-server 实现
初始化
OAuth2.0 只是协议,在实现上需要联系到用户与数据库存储,oauth2-server 的新版本并没有指定某种数据库,但它提供了 Interfaces 与 Traits 帮助我们实现,这让我们可以方便的使用任何形式的数据存储方式,这种方便的代价就是需要我们自行创建 Repositories 与 Entities。
初始化 server
// 初始化存储库 $clie