biohivetech / laravel-introspection
Laravel Introspection - 为 Laravel Passport 提供OAuth Introspection功能
Requires
- php: ^7.2
- guzzlehttp/guzzle: ^6.0
- laravel/passport: ^8.0
This package is auto-updated.
Last update: 2020-09-02 22:38:10 UTC
README
注意
此包已拆分为两部分,以分离授权和资源服务器的组件。此包将保留,可能会归档。
开发构建和文档现已上线!您可以在以下位置找到它们: https://github.com/DataHiveDevelopment/passport-introspection-docs
介绍
在您的资源和授权服务器上安装此包。Passport 也应安装在并配置在您的 授权服务器 上。Passport 是此包的依赖项,因此它将安装在您的资源服务器上,但您不需要配置它。请在资源服务器上使用本文档中概述的方法代替 Passport。
谢谢 :)
# 关于此包
此包提供了一个 Introspection 控制器、认证守卫和中间件,允许您托管独立的授权和资源服务器。
一包两用。
# 安装
composer require datahivedevelopment/laravel-introspection
安装此包,并根据您配置的服务器类型执行以下适当的步骤。
如果您想使用 UUID 作为跨应用程序中用户的唯一标识符,就像我们一样,请遵循 UUID 设置。您将只在授权服务器上执行此设置!
# 授权服务器
来自 https://oauth2.thephpleague.com/terminology/
在成功验证客户端和资源所有者,并授权请求后发行访问令牌的服务器。
这是运行 Passport 的服务器,它是用户信息的中心权威机构,并负责验证从资源服务器传递给它的访问令牌。
首先,如果您尚未安装Passport,请按照官方文档进行安装和标准配置。在我们的默认配置中,您需要启用Client Credentials
授权类型。请参阅Passport文档的客户端凭证授权部分以获取添加中间件的说明。
接下来,您应该在您的AuthServiceProvider
的boot
方法中调用Introspection::routes
方法,在调用Passport
之后。
App\Providers\AuthServiceProvider.php
<?php use DataHiveDevelopment\Introspection\Introspection; // ... public function boot() { $this->registerPolicies(); Passport::routes(); Passport::tokensCan([ 'user.read' => '...', //... ]); Introspection::routes(); }
您可以通过将选项传递给Introspection::routes
调用来更改内省端点前缀,从默认的/oauth
。
Introspection::routes([ 'prefix' => '/apiauth' ]);
在上面的示例中,内省端点将变为https://myauthserver.test/apiauth/introspect
。
# 资源服务器
来自 https://oauth2.thephpleague.com/terminology/
位于受保护资源(例如“推文”、用户照片或个人数据)之前的服务器,并且能够接受和响应用户端对受保护资源的请求。
在您的config/auth.php
配置文件中,您应该将api
身份验证守卫的driver
选项设置为introspect
。
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'introspect', 'provider' => 'users', ], ],
将以下内容添加到您的.env
文件中
INTROSPECTION_TOKEN_URL=https://authserver.test/oauth/token
INTROSPECTION_TOKEN_SCOPES="Introspect"
INTROSPECTION_ENDPOINT=https://authserver.test/oauth/introspect
INTROSPECTION_CLIENT_ID=
INTROSPECTION_CLIENT_SECRET=
内省端点目前仅支持通过客户端凭证授权的Bearer
令牌进行身份验证。使用php artisan passport:client --client
在您的Passport服务器上生成客户端凭证OAuth客户端,并将详细信息输入到您的.env
文件下的INTROSPECTION_CLIENT_ID
和INTROSPECTION_CLIENT_SECRET
中。
INTROSPECTION_TOKEN_SCOPES
选项应该是一个带引号的、用空格分隔的列表,列出您希望内省客户端在与INTROSPECTION_ENDPOINT
通信时使用的范围。
如果未定义或留空,则Passport将假定您未请求任何范围。我建议您创建并使用一个Introspect
范围,并将其分配给内省路由,但这完全是可选的,具体取决于您的使用情况。
例如,DataHive目前只为内部应用程序颁发客户端凭证授权类型,我们默认使用client
中间件保护内省路由。因此,我们不需要额外的验证来保护路由。
如果想要使用范围来保护路由,您需要修改您的授权服务器上的Introspection::routes
调用。下面,我们使用以下示例中的Passport scope
中间件
Introspection::routes([ 'middleware' => [ 'client', 'scope:Introspect' ] ]);
我建议保留client
中间件,除非您实现了其他身份验证方法。请参阅OAuth Introspection RFC以获取保护内省端点的详细信息。
Passport Trait
提醒一下,请按照Passport文档将use HasAccessToken;
特质添加到您的User模型中。
# 保护路由
以下操作将在您的资源服务器上执行。
# 中间件
本软件包包含我们自己的护照式 认证守卫,该守卫将验证传入请求的访问令牌。一旦您将 api
守卫配置为使用 introspect
驱动程序,您只需在需要有效访问令牌的任何路由上指定 auth:api
中间件,类似于 Passport。
routes/api.php
Route::get('/orders', function () { // })->middleware('auth:api');
# 令牌作用域
我们包含的作用域检查中间件几乎与 Passport 完全相同。实际上,如果您曾经阅读过 Passport 文档,以下文档可能会听起来非常熟悉。
要开始使用,请将以下中间件添加到您的 app/Http/Kernel.php
文件的 $routeMiddleware
属性中
'scopes' => \DataHiveDevelopment\Introspection\Http\Middleware\CheckScopes::class, 'scope' => \DataHiveDevelopment\Introspection\Http\Middleware\CheckForAnyScope::class,
# Scopes
中间件
Route::get('/orders', function () { // Access token has both "check-status" and "place-orders" scopes... })->middleware('scopes:check-status,place-orders');
# Scope
中间件
Route::get('/orders', function () { // Access token has either "check-status" or "place-orders" scope... })->middleware('scope:check-status,place-orders');
# UUID 设置
在您的授权服务器上安装以下软件包
composer require dyrynda/laravel-model-uuid composer require dyrynda/laravel-efficient-uuid
根据软件包的文档实现必要模型上的列和特性。此软件包目前仅支持默认的 uuid
列名,因此当涉及到数据库迁移时,请不要更改任何内容。这些软件包将使您在数据库中存储 UUID 的二进制形式变得容易。
$table->efficientUuid('uuid')->index();
# 为什么使用 UUID?
此软件包是为 DataHive 的特定需求而设计的。因此,它假设存在“无共享”数据库实现。每个应用程序维护一个用户列表,这些用户都可以链接回主认证应用程序。
# DataHive 用法的简要背景
我们使用我们自己的 Socialite 提供程序在我们的资源应用程序上对用户进行认证,使用我们的主用户认证应用程序。此服务也是拥有 Passport、颁发 OAuth 令牌并管理 OAuth 客户端的系统。
在我们的资源应用程序中,我们有以下 user
表模式:
$table->bigIncrements('id'); $table->uuid('uuid')->index(); $table->rememberToken(); $table->timestamps();
是的,我知道 UUID 列目前不一定高效。当我们使用 UUID 软件包在资源服务器上创建新用户时,我还未解决一个无关的问题,所以我们目前没有在资源服务器上使用 Michael 的软件包。
以及在我们的中央用户认证服务器(OAuth 授权服务器)上:
$table->bigIncrements('id'); $table->efficientUuid('uuid')->index(); $table->string('name'); $table->string('password'); // additional columns etc $table->rememberToken(); $table->timestamps();
我们使用 Michael Dyrynda 的 Laravel Efficient UUID 软件包在我们的数据库表上提供 efficientUuid
方法,以及他的 Laravel Model UUID 软件包提供类型转换和模型方法,通过 UUID 查找条目。
由于我们使用 Socialite 进行用户认证,所以我不能使用内置的 Eloquent UserProvider 的 方法来检索用户,因为我们希望使用自定义列来查找用户。相反,我实例化了用户模型(由 auth.providers.[guards.api.provider].model'
提供,默认为 App\User::class
),这样我就可以利用 User::whereUuid(...)
方法。
UUID 在 introspection 控制器的响应中返回,以便 introspection 守卫知道尝试匹配用户的哪个 UUID。
来自 /oauth/introspect
端点的示例响应
{ "active": true, "scope": "user.read", "client_id": 4, "token_type": "access_token", "exp": 1606091154, "iat": 1574468754, "nbf": 1574468754, "sub": 1, "aud": 4, "jti": "702481dc66b64bd1eee41be4e20e2d3170ac509b6d47b3cab50d8fbef83f73d1b637080b5a0cdd47", "id": "11e17430-9710-4033-be5d-12e0d182f8f3", }
# 覆盖
您可以通过在您的用户模型上定义一个 findForIntrospect()
方法来覆盖资源服务器如何检索用户。
public function findForIntrospect($userId) { return $this->where('username', $userId)->first(); }
此方法应根据您希望使用的任何标准返回与 ID 匹配的模型。在此示例中,我们根据用户名进行匹配。
注意:我不建议使用非静态、用户可更改的字段,因为这会使跨系统匹配用户变得不可能。上面的内容仅作为示例。
对于授权服务器,您需要实现一个返回您在资源服务器上使用的唯一用户 ID 的 getIntrospectionUserId()
方法,该 ID 将与授权服务器上的身份关联。此 ID 将作为上面的 id
声明返回。
public function getIntrospectionUserId() { return $this->username; }
如果您通过使用 getIntrospectionUserId()
方法覆盖了我们的默认 UUID 查找,那么您需要在资源服务器的用户模型上实现相应的 findForIntrospect()
方法。
如果您没有定义上述方法,则包默认使用 whereUuid()
方法,因此请确保您选择并实现了您希望使用的方法。
# 使用 JavaScript 消费您的资源服务器 API
类似于 Passport,我创建了一个 CreateFreshApiToken
中间件,您可以将其实现以从您的前端 JavaScript 进行 API 调用。我建议您阅读 官方文档 以更好地了解此用法。
此令牌目前无法在资源服务器之间工作,只能用于调用与 JavaScript 调用相同的资源服务器上发布的 API。
您只需要将 CreateFreshApiToken
中间件添加到您的 app/Http/Kernel.php
文件中的 web
中间件组中。
'web' => [ // Other middleware... \DataHiveDevelopment\Introspection\Http\Middleware\CreateFreshApiToken::class, ],
注意:与 Passport 一样,您应该确保 CreateFreshApiToken
中间件是列表中最后一个中间件。
您可以使用 Introspection::cookie
方法自定义从默认 laravel_token
开始的 cookie 名称。通常,您会从您的 AuthServiceProvider
的 boot
方法中调用此操作。
/** * Register any authentication / authorization services. * * @return void */ public function boot() { $this->registerPolicies(); Introspection::routes(); Introspection::cookie('myapp_token'); }
# CSRF 保护
我们已经实现了与 Passport 相同样式的 cookie 和 CSRF 保护。默认的 Laravel JavaScript 框架包括一个 Axios 实例,该实例将自动使用加密的 XSRF-TOKEN
cookie 值在同源请求上发送 X-XSRF-TOKEN
标头。