biohivetech/laravel-introspection

此包已被弃用且不再维护。作者建议使用 datahivedevelopment/laravel-introspection 包代替。

Laravel Introspection - 为 Laravel Passport 提供OAuth Introspection功能

v2.1.0 2020-03-16 13:41 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文档的客户端凭证授权部分以获取添加中间件的说明。

接下来,您应该在您的AuthServiceProviderboot方法中调用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_IDINTROSPECTION_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 名称。通常,您会从您的 AuthServiceProviderboot 方法中调用此操作。

/**
 * 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 标头。