lookyman / nette-oauth2-server-doctrine
Nette 框架中集成 The League of Extraordinary Packages' OAuth 2.0 服务器 - Kdyby/Doctrine 存储实现
Requires
- php: ^7.1
- kdyby/doctrine: ^3.3
- kdyby/events: ^3.1, >=3.1.2
- league/oauth2-server: ^7.0
- lookyman/nette-oauth2-server: ^3.0
- nette/application: ^2.4, >=2.4.9
- nette/caching: ^2.5, >=2.5.6
- nette/di: ^2.4, >=2.4.10
Requires (Dev)
- jakub-onderka/php-parallel-lint: ^1.0
- lookyman/coding-standard: ^0.1.0
- nette/bootstrap: ^2.4, >=2.4.5
- phpstan/phpstan: ^0.9.2
- phpstan/phpstan-nette: ^0.9.0
- phpstan/phpstan-phpunit: ^0.9.4
- phpstan/phpstan-strict-rules: ^0.9.0
- phpunit/phpunit: ^7.0
README
将 The League of Extraordinary Packages' 非凡包 的 OAuth 2.0 服务器 集成到 Nette 框架 中 - Kdyby/Doctrine 存储实现。
安装
无聊的部分
阅读 这篇文档。从头到尾,真的,不要跳过它然后回来抱怨某些东西不起作用。
如果您尚未安装和配置 Kdyby/Doctrine,请务必安装。
使用 Composer 进行安装
composer require lookyman/nette-oauth2-server-doctrine
设置路由
根据您想要支持的授权类型,您必须设置路由到 access_token
、authorize
或这两个端点。
- 对于除
隐式
之外的所有授权,设置access_token
端点路由。 - 对于
授权码
或隐式
授权,设置authorize
端点路由。
端点位于 NetteOAuth2Server:OAuth2:accessToken
和 NetteOAuth2Server:OAuth2:authorize
映射,设置可能如下所示
class RouterFactory { /** * @return IRouter */ public static function createRouter() { $router = new RouteList(); $router[] = new Route('oauth2/<action>', 'NetteOAuth2Server:OAuth2:default'); // ... return $router; } }
您可以通过 https://myapp.com/oauth2/access-token
和 https://myapp.com/oauth2/authorize
URL 分别访问这些端点。
配置
extensions: oauth2: Lookyman\NetteOAuth2Server\Storage\Doctrine\NetteOAuth2ServerDoctrineExtension oauth2: grants: authCode: on clientCredentials: on implicit: on password: on refreshToken: on privateKey: /path/to/private.key publicKey: /path/to/public.key encryptionKey: '32 base64encoded random bytes' approveDestination: :Approve: loginDestination: :Sign:in tablePrefix: nette_oauth2_server_ loginEventPriority: 0
grants
部分包含您想要启用的授权。默认情况下,它们都是禁用的,因此您只需输入您想要使用的内容。每个值不必只是一个布尔值。您可以指定一个令牌 TTL,例如:[ttl: PT1H]
。其中两种授权还有额外的设置。《授权码》授权有 authCodeTtl
选项,而《隐式》授权有 accessTokenTtl
选项。在这些情况下,指定间隔的格式遵循 此处 描述的格式。
《授权码》授权还有一个选项来启用对 RFC 7636 的支持。您可以通过指定 [pkce: on]
来打开它。
接下来,您需要一对私钥/公钥。如果您没有跳过第一步,您应该知道如何进行。如果您跳过了,现在是时候了。去读它,拿到密钥后再回来,并在 privateKey
和 publicKey
选项中输入路径。如果您的私钥受密码保护,请按照以下方式指定:privateKey: [keyPath: /path/to/private.key, passPhrase: passphrase]
。
此外,您还需要提供一个加密密钥。最简单的方法是从终端运行 php -r 'echo base64_encode(random_bytes(32));'
并将结果粘贴到 encryptionKey
选项中。
如果您正在使用Authorization Code
或Implicit
授权,您需要设置重定向目的地。这些应该是您在$presenter->redirect()
方法中使用的普通字符串。下面将详细讨论approveDestination
。loginDestination
应指向包含应用程序登录表单的presenter/action。两个路径都应该是绝对路径(带有模块)。
如果您不使用Authorization Code
或Implicit
授权,可以省略approveDestination
和loginDestination
选项。
tablePrefix
选项允许您设置生成表的表前缀。默认值是nette_oauth2_server_
。
最后,当使用Authorization Code
或Implicit
授权时,用户会在某个时刻被重定向到登录页面。这种重定向是通过监听Nette\Security\User::onLoggedIn
事件的订阅者来完成的,但如果您已经有其他订阅者正在监听它,您可能需要调整事件优先级。您可以使用loginEventPriority
选项来做到这一点。
更新数据库模式
php www/index.php orm:schema-tool:update --force
您可能希望使用--dump-sql
而不是--force
,并手动运行生成的SQL查询。但如果您的数据库模式之前与映射同步,这应该是安全的。
它将在数据库中生成7个新表
nette_oauth2_server_access_token
nette_oauth2_server_access_token_scope
nette_oauth2_server_auth_code
nette_oauth2_server_auth_code_scope
nette_oauth2_server_client
nette_oauth2_server_refresh_token
nette_oauth2_server_scope
实现特性
最后一部分(也是最有趣的部分)是将所有这些集成到您的应用程序中。为此,有一个方便的特性可以轻松完成这个过程。此外,此步骤仅适用于您想使用Authorization Code
或Implicit
授权的情况,所以如果您不这样做,您已经完成了。太棒了!
您需要创建一个批准presenter。还记得配置中的approveDestination
选项吗?这就是它的作用。presenter应使用Lookyman\NetteOAuth2Server\UI\ApprovePresenterTrait
特性,并在approveDestination
选项指向的动作中调用$this['approve']
。它应该看起来像这样
class ApprovePresenter extends Presenter { use ApprovePresenterTrait; // ... public function actionDefault() { $this['approve']; } }
当然,您不必为此创建一个新的presenter。如果您愿意,可以在现有的presenter中使用此特性。只需确保在配置中设置正确的approveDestination
并在动作中初始化组件为$this['approve']
即可。
最后,该动作需要一个模板。因此,在presenter的动作正确目的地创建一个Latte模板文件,并将其中的单行放在里面
{control approve}
如你所见,整个过程高度可配置。这样做是为了让您完全控制自己的应用程序,并将艰苦的工作留给包。
完成设置
此包不提供管理客户端应用程序、访问令牌或作用域的方法。您必须自己实现这些。但是,您可以使用此包提供的实体和存储库。
- 访问令牌
Lookyman\NetteOAuth2Server\Storage\Doctrine\AccessToken\AccessTokenEntity
Lookyman\NetteOAuth2Server\Storage\Doctrine\AccessToken\AccessTokenRepository
- 认证代码
Lookyman\NetteOAuth2Server\Storage\Doctrine\AuthCode\AuthCodeEntity
Lookyman\NetteOAuth2Server\Storage\Doctrine\AuthCode\AuthCodeRepository
- 客户端
Lookyman\NetteOAuth2Server\Storage\Doctrine\Client\ClientEntity
Lookyman\NetteOAuth2Server\Storage\Doctrine\Client\ClientRepository
- 刷新令牌
Lookyman\NetteOAuth2Server\Storage\Doctrine\RefreshToken\RefreshTokenEntity
Lookyman\NetteOAuth2Server\Storage\Doctrine\RefreshToken\RefreshTokenRepository
- 作用域
Lookyman\NetteOAuth2Server\Storage\Doctrine\Scope\ScopeEntity
Lookyman\NetteOAuth2Server\Storage\Doctrine\Scope\ScopeRepository
至少,您应该创建一种注册客户端应用程序的方法。除非您只想手动在数据库中完成。
保护资源
本软件包提供了一个抽象的 Lookyman\NetteOAuth2Server\UI\ResourcePresenter
,您可以使用它来保护您的资源。它的 checkRequirements()
方法验证访问令牌,并使用修改后的 Psr\Http\Message\ServerRequestInterface
对象触发一个 onAuthorized
事件。如果验证成功,以下属性将被设置在它上面:
oauth_access_token_id
- 访问令牌标识符,oauth_client_id
- 客户端标识符,oauth_user_id
- 由访问令牌表示的用户标识符,oauth_scopes
- 字符串作用域标识符数组。
高级使用
自定义审批模板
审批组件的模板已准备好使用 Bootstrap,但可以通过一些特质魔法进行更改
class ApprovePresenter extends Presenter { use ApprovePresenterTrait { createComponentApprove as ___createComponentApprove; } // ... /** * @return ApproveControl */ protected function createComponentApprove() { $control = $this->___createComponentApprove(); $control->setTemplateFile(__DIR__ . '/path/to/template.latte'); return $control; } }
模板传递一个变量 $authorizationRequest
,其中包含一个 League\OAuth2\Server\RequestTypes\AuthorizationRequest
对象,其中包含有关正在批准的请求的信息。
自定义授权
自定义授权必须实现 League\OAuth2\Server\Grant\GrantTypeInterface
。在您的 config.neon
中启用它们,如下所示
services: - MyCustomGrant oauth2.authorizationServer: setup: - enableGrantType(@MyCustomGrant)
日志记录
此软件包支持标准 PSR-3 日志记录。如果您已注册符合规范的记录器作为服务,则最简单的方法是通过 config.neon
启用它
decorator: Psr\Log\LoggerAwareInterface: setup: - setLogger
客户端密钥验证
默认情况下,Lookyman\NetteOAuth2Server\Storage\Doctrine\Client\ClientRepository
使用简单的 hash_equals
函数来验证客户端密钥。这意味着它期望数据库中的密钥以纯文本形式存储,这可能是出于明显的原因而并非最佳选择。因此,强烈建议您将密钥哈希化(例如使用 password_hash()
),并实现您自己的密钥验证器
class SecretValidator { public function __invoke($expected, $actual) { return password_verify($actual, $expected); } }
然后在配置中注册它
services: - SecretValidator oauth2.repository.client: arguments: [secretValidator: @SecretValidator]
用户凭据验证
Lookyman\NetteOAuth2Server\User\UserRepository
通过尝试登录用户来验证用户凭据。但是,如果您的登录过程以某种方式被修改,它可能会以意外的方式轻松失败。在这种情况下,您可能需要重新实现凭据验证器。只需以您应用程序的方式获取正确的用户 ID,然后返回 Lookyman\NetteOAuth2Server\User\UserEntity
(或 null
,如果凭据无效)。
class CredentialsValidator { public function __invoke($username, $password, $grantType, ClientEntityInterface $clientEntity) { // get the user ID from your application, and return new UserEntity($userId); } }
然后在配置中注册它
services: - CredentialsValidator oauth2.repository.user: arguments: [credentialsValidator: @CredentialsValidator]
修改作用域
在颁发访问令牌之前,您可以修改请求的作用域。默认情况下,令牌将使用请求的作用域颁发,但您可以使用自定义终结器更改这一点
class ScopeFinalizer { public function __invoke(array $scopes, $grantType, ClientEntityInterface $clientEntity, $userIdentifier) { return $scopes; // this is the default behavior } }
然后在配置中注册它
services: - ScopeFinalizer oauth2.repository.scope: arguments: [scopeFinalizer: @ScopeFinalizer]