Laravel Passport通过访问令牌获取客户端ID

Wal*_*mar 11 php laravel laravel-5 laravel-passport

我正在编写一个小sms网关,供几个项目使用,

我实施了laravel passport身份验证(客户端凭据授权令牌)

然后我添加CheckClientCredentials到api中间件组:

protected $middlewareGroups = [
    'web' => [
       ...
    ],

    'api' => [
        'throttle:60,1',
        'bindings',
        \Laravel\Passport\Http\Middleware\CheckClientCredentials::class
    ],
];
Run Code Online (Sandbox Code Playgroud)

逻辑工作正常,现在在我的控制器中我需要让客户端与有效令牌相关联.

routes.php文件

Route::post('/sms', function(Request $request) {
    // save the sms along with the client id and send it

    $client_id = ''; // get the client id somehow

    sendSms($request->text, $request->to, $client_id);
});
Run Code Online (Sandbox Code Playgroud)

出于明显的安全原因,我永远不能发送带有消费者请求的客户端ID,例如$client_id = $request->client_id;.

bma*_*ovu 10

我用它来访问经过身份验证的客户端应用程序...

$bearerToken = $request->bearerToken();
$tokenId = (new \Lcobucci\JWT\Parser())->parse($bearerToken)->getHeader('jti');
$client = \Laravel\Passport\Token::find($tokenId)->client;

$client_id = $client->id;
$client_secret = $client->secret;
Run Code Online (Sandbox Code Playgroud)

资源


Dan*_*ers 10

然而,答案已经很晚了,我在 Laravel 6.x 中提取 JTI 标头时遇到了一些错误,因为 JTI 不再位于标头中,而仅位于有效载荷/声明中。(使用客户赠款)

local.ERROR: Requested header is not configured {"exception":"[object] (OutOfBoundsException(code: 0): Requested header is not configured at /..somewhere/vendor/lcobucci/jwt/src/Token.php:112)
Run Code Online (Sandbox Code Playgroud)

此外,将它添加到中间件中对我来说不是一个选择。因为我在我的应用程序的几个地方需要它。

所以我扩展了原始的 Laravel Passport Client (oauth_clients) 模型。并检查标头和有效载荷。如果没有请求被传递,则允许传递请求,或使用请求门面。

<?php

namespace App\Models;

use Illuminate\Support\Facades\Request as RequestFacade;
use Illuminate\Http\Request;
use Laravel\Passport\Client;
use Laravel\Passport\Token;
use Lcobucci\JWT\Parser;

class OAuthClient extends Client
{
    public static function findByRequest(?Request $request = null) : ?OAuthClient
    {
        $bearerToken = $request !== null ? $request->bearerToken() : RequestFacade::bearerToken();

        $parsedJwt = (new Parser())->parse($bearerToken);

        if ($parsedJwt->hasHeader('jti')) {
            $tokenId = $parsedJwt->getHeader('jti');
        } elseif ($parsedJwt->hasClaim('jti')) {
            $tokenId = $parsedJwt->getClaim('jti');
        } else {
            Log::error('Invalid JWT token, Unable to find JTI header');
            return null;
        }

        $clientId = Token::find($tokenId)->client->id;

        return (new static)->findOrFail($clientId);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你可以在你的 Laravel 应用程序中的任何地方使用它,如下所示:

如果您有可用的 $request 对象,(例如来自控制器)

$client = OAuthClient::findByRequest($request);
Run Code Online (Sandbox Code Playgroud)

或者即使请求以某种方式不可用,您也可以不使用它,如下所示:

$client = OAuthClient::findByRequest();
Run Code Online (Sandbox Code Playgroud)

希望这对今天面临这个问题的任何人都有用。


小智 9

有一种棘手的方法.您可以在中间件CheckClientCredentials中修改句柄方法,只需添加此行即可.

        $request["oauth_client_id"] = $psr->getAttribute('oauth_client_id');
Run Code Online (Sandbox Code Playgroud)

然后你可以在控制器的函数中获得client_id:

public function info(\Illuminate\Http\Request $request)
{
    var_dump($request->oauth_client_id);
}
Run Code Online (Sandbox Code Playgroud)


小智 5

OAuth 令牌和客户端信息作为受保护的变量存储在 Laravel\Passport\HasApiTokens 特征(您添加到用户模型中)中。

因此,只需在您的User 模型中添加一个 getter 方法来公开 OAuth 信息:

public function get_oauth_client(){
  return $this->accessToken->client;
}
Run Code Online (Sandbox Code Playgroud)

这将为 oauth_clients 表返回一个 Eloquent 模型


Wal*_*mar 3

所以,没有答案...

我能够通过使用我自己的 API 来解决这个问题,最后我想出了更简单的身份验证流程,客户端需要在每个请求中发送他们的 id 和密钥,然后受Esben Petersen/oauth/token的启发,我使用发送的凭据使用我自己的路由博客文章

生成访问令牌后,我将其附加到Symfony\Request正在处理的实例的标头中。

我的最终输出是这样的:

<?php

namespace App\Http\Middleware;

use Request;

use Closure;

class AddAccessTokenHeader
{
    /**
     * Octipus\ApiConsumer
     * @var ApiConsumer
     */
    private $apiConsumer;


    function __construct() {
        $this->apiConsumer  = app()->make('apiconsumer');
    }

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $this->apiConsumer->post('/oauth/token', $request->input(), [
            'content-type' => 'application/json'
        ]);


        if (!$response->isSuccessful()) {
            return response($response->getContent(), 401)
                    ->header('content-type', 'application/json');
        }

        $response = json_decode($response->getContent(), true);

        $request->headers->add([
            'Authorization'     => 'Bearer ' . $response['access_token'],
            'X-Requested-With'  => 'XMLHttpRequest'
        ]);

        return $next($request);

    }
}
Run Code Online (Sandbox Code Playgroud)

我将上述中间件与 Passport 的CheckClientCredentials.

protected $middlewareGroups = [
    'web' => [
        ...
    ],

    'api' => [
        'throttle:60,1',
        'bindings',
        \App\Http\Middleware\AddAccessTokenHeader::class,
        \Laravel\Passport\Http\Middleware\CheckClientCredentials::class
    ],
];
Run Code Online (Sandbox Code Playgroud)

通过这种方式,我能够确保这$request->input('client_id')是可靠的并且不会被伪造。

  • 所以你不是首先获取客户端令牌,而是总是发送 client_id + Secret?这不是它应该如何工作的。 (3认同)