datahivedevelopment / 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-10-02 22:57:34 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 特性
提醒一下,请按照Passport 文档将use HasAccessToken;
特性添加到您的User模型中。
# 保护路由
以下操作将在您的资源服务器上执行。
# 中间件
本软件包包含我们自己的护照式认证保护器,它将在传入请求中验证访问令牌。一旦您已将api
保护器配置为使用introspect
驱动程序,您只需在需要有效访问令牌的任何路由上指定auth:api
中间件,类似于护照。
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匹配的模型,基于您想要使用的任何标准。在这个例子中,我们基于用户名进行匹配。
注意:我不建议使用非静态、可由用户更改的字段,因为这会使跨系统匹配用户变得不可能。上面的只是一个例子。
对于授权服务器,您需要实现一个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
头。