7

我正在尝试使用 Laravel Passport 设置多重身份验证,但它似乎不支持它。我正在使用密码授予来颁发令牌,这需要我传递想要访问令牌的用户的用户名/密码。

我有 3 个 auth guards/providers 设置,总共 4 个。用户、供应商、管理员和 API

其中 2 个 Auths 需要通行证访问权限,因此每个用户都需要能够颁发令牌。但是 Passport 会自动采用 API 身份验证提供程序,但我希望根据登录的用户来更改它。如果用户这样做,则User 提供程序,如果它是供应商,则Vendor 提供程序

但是 Passport 目前只支持 1 种用户类型,因此默认为API 提供者

有更好的办法吗?还是我应该使用基于角色的身份验证。

4

7 回答 7

6

如果你还需要。

我更喜欢角​​色,有一个很棒的插件:https ://github.com/larapacks/authorization

但是,如果您以某种方式需要它,您将能够按照以下步骤使用。

对于多守卫,您将不得不覆盖一些代码。

您可以创建自己的 PassportServiceProvider 并扩展 PassportServiceProvider 并覆盖方法 makePasswordGrant,而不是加载 PassportServiceProvider。在此方法中,您将为您自己的扩展存储库更改 Passport UserRepository。在用户存储库中,您必须将静态模型配置更改为动态模型配置(我从请求属性加载,但您可以从任何地方获取)。

您可能必须覆盖其他内容,但我进行了测试并且有效。

前任:

PassportServiceProvider

namespace App\Providers;

use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Grant\PasswordGrant;
use Laravel\Passport\PassportServiceProvider as BasePassportServiceProvider;
use Laravel\Passport\Passport;

class PassportServiceProvider extends BasePassportServiceProvider
{
    /**
     * Create and configure a Password grant instance.
     *
     * @return PasswordGrant
     */
    protected function makePasswordGrant()
    {
        $grant = new PasswordGrant(
            $this->app->make(\App\Repositories\PassportUserRepository::class),
            $this->app->make(\Laravel\Passport\Bridge\RefreshTokenRepository::class)
        );

        $grant->setRefreshTokenTTL(Passport::refreshTokensExpireIn());

        return $grant;
    }

}

用户存储库

namespace App\Repositories;

use App;
use Illuminate\Http\Request;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use Laravel\Passport\Bridge\UserRepository;
use Laravel\Passport\Bridge\User;
use RuntimeException;

class PassportUserRepository extends UserRepository
{
    /**
     * {@inheritdoc}
     */
    public function getUserEntityByUserCredentials($username, $password, $grantType, ClientEntityInterface $clientEntity)
    {
        $guard = App::make(Request::class)->attributes->get('guard') ?: 'api';
        $provider = config("auth.guards.{$guard}.provider");


        if (is_null($model = config("auth.providers.{$provider}.model"))) {
            throw new RuntimeException('Unable to determine user model from configuration.');
        }


        if (method_exists($model, 'findForPassport')) {
            $user = (new $model)->findForPassport($username);
        } else {
            $user = (new $model)->where('email', $username)->first();
        }


        if (! $user ) {
            return;
        } elseif (method_exists($user, 'validateForPassportPasswordGrant')) {
            if (! $user->validateForPassportPasswordGrant($password)) {
                return;
            }
        } elseif (! $this->hasher->check($password, $user->password)) {
            return;
        }

        return new User($user->getAuthIdentifier());
    }
}

PS:对不起我的英语不好。

于 2017-03-11T19:54:34.250 回答
4

您必须修改主库文件。

1) 文件:vendor\laravel\passport\src\Bridge\UserRepository.php

找到getUserEntityByUserCredentials并复制完整的方法并将此方法粘贴到下面,名称为getEntityByUserCredentials。不要修改 main 函数,因为它在某处使用。

//Add the $provider variable at last or replace this line.
public function getEntityByUserCredentials($username, $password, $grantType, ClientEntityInterface $clientEntity, $provider)

然后,在新的复制函数中,找到以下内容:

$provider = config('auth.guards.api.provider');

并将其替换为:

$provider = config('auth.guards.'.$provider.'.provider');

2) 文件:vendor\league\oauth2-server\src\Grant\PasswordGrant.php

在函数validateUser中,在行号之后添加以下代码。88

$provider = $this->getRequestParameter('provider', $request);

if (is_null($provider)) {
     throw OAuthServerException::invalidRequest('provider');
}

添加后将以下代码替换为

$user = $this->userRepository->getEntityByUserCredentials(
        $username,
        $password,
        $this->getIdentifier(),
        $client,
        $provider
    );

现在尝试使用邮递员

在您的输入字段中添加提供者字段,例如

provider = api_vendors
OR
provider = api_admins
OR
provider = api_users
And so on....

确保您已添加您的提供程序并在 config/auth.php 中设置驱动程序

'guards' => [
 'api_admins' => [
    'driver' => 'passport',
    'provider' => 'admins',
  ],
  'api_vendors' => [
    'driver' => 'passport',
    'provider' => 'vendors',
  ],

我希望这有帮助。

于 2017-06-23T00:05:40.620 回答
2

我为这个问题创建了一个小包。这是完整文档链接的链接

但要点是,每当一个user实体登录时,它都会检查守卫和提供者。

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],

    'customers' => [
        'driver' => 'passport',
        'provider' => 'customers'
    ],
],

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => 'App\User',
    ],
    /**
     * This is the important part. You can create as many providers as you like but right now, 
     * we just need the customer
     */
     'customers' => [
         'driver' => 'eloquent',
         'model' => 'App\Customer',
     ],
],

你应该有一个这样的控制器:

<?php

namespace App\Http\Controllers\Auth;

use App\Customers\Customer;
use App\Customers\Exceptions\CustomerNotFoundException;
use Illuminate\Database\ModelNotFoundException;
use Laravel\Passport\Http\Controllers\AccessTokenController;
use Laravel\Passport\TokenRepository;
use League\OAuth2\Server\AuthorizationServer;
use Psr\Http\Message\ServerRequestInterface;
use Lcobucci\JWT\Parser as JwtParser;

class CustomerTokenAuthController extends AccessTokenController
{
     /**
      * The authorization server.
      *
      * @var \League\OAuth2\Server\AuthorizationServer
      */
     protected $server;

     /**
      * The token repository instance.
      *
      * @var \Laravel\Passport\TokenRepository
      */
     protected $tokens;

     /**
      * The JWT parser instance.
      *
      * @var \Lcobucci\JWT\Parser
      */
     protected $jwt;

     /**
      * Create a new controller instance.
      *
      * @param  \League\OAuth2\Server\AuthorizationServer  $server
      * @param  \Laravel\Passport\TokenRepository  $tokens
      * @param  \Lcobucci\JWT\Parser  $jwt
      */
     public function __construct(AuthorizationServer $server,
                                 TokenRepository $tokens,
                                 JwtParser $jwt)
     {
         parent::__construct($server, $tokens, $jwt);
     }

     /**
      * Override the default Laravel Passport token generation
      *
      * @param ServerRequestInterface $request
      * @return array
      * @throws UserNotFoundException
      */
     public function issueToken(ServerRequestInterface $request)
     {
         $body = (parent::issueToken($request))->getBody()->__toString();
         $token = json_decode($body, true);

         if (array_key_exists('error', $token)) {
             return response()->json([
                 'error' => $token['error'],
                 'status_code' => 401
             ], 401);
         }

        $data = $request->getParsedBody();

        $email = $data['username'];  

        switch ($data['provider']) {
            case 'customers';

                try {

                 $user = Customer::where('email', $email)->firstOrFail();

                } catch (ModelNotFoundException $e) {
                  return response()->json([
                      'error' => $e->getMessage(),
                      'status_code' => 401
                  ], 401);
                }

                break;

            default :

                try {

                 $user = User::where('email', $email)->firstOrFail();

                } catch (ModelNotFoundException $e) {
                  return response()->json([
                      'error' => $e->getMessage(),
                      'status_code' => 401
                  ], 401);
                }        
        }

        return compact('token', 'user');
    }
}

并且请求应该是:

POST /api/oauth/token HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache

grant_type=password&username=test%40email.com&password=secret&provider=customers

要检查您的控制器谁是登录用户,您可以执行以下操作:

auth()->guard('customers')->user()

于 2017-10-18T12:58:28.560 回答
2

我已经在Laravel 7中完成了它,没有任何自定义代码,正如其他答案所建议的那样。我刚刚更改了3个文件如下

config/auth.php文件(我的新表名是doctors

'guards' => [
   ...
    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
        'hash' => false,
    ],
    'api-doctors' => [
        'driver' => 'passport',
        'provider' => 'doctors',
    ],
],

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\User::class,
    ],
    
    'doctors' => [
        'driver' => 'eloquent',
        'model' => App\Doctor::class,
    ],
],

修改我的模型与模型 ( )Doctor类似UserApp/Doctor.php

....
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;

class Doctor extends Authenticatable
{
  use HasApiTokens, Notifiable;

最后使用中间件文件定义路由routes/api.php如下

//normal middleware which exist already
Route::post('/choose', 'PatientController@appointment')->middleware('auth:api');

//newly created middleware provider (at config/auth.php)
Route::post('/accept', 'Api\DoctorController@allow')->middleware('auth:api-doctors');

现在,当您将创建新的 oauth 客户端时,您可以使用artisan passport:client --password --provider此命令在命令行中提示您选择表,然后不要忘记运行

php artisan config:cache
php artisan cache:clear

您也可以通过将列值替换为在oauth_clients表中手动创建用户providerusersdoctors

参考链接中的一些提示 https://laravel.com/docs/7.x/passport#customizing-the-user-provider

于 2020-08-26T16:15:12.617 回答
1

我们正在等待 Laravel 将这个功能添加到它的包中,但是对于那些想要添加这个功能的人,我建议使用这个

于 2019-02-13T17:04:40.730 回答
0

如果您正在寻找Passport Multi-Auth API的解决方案, 我向您推荐这个解决方案

于 2019-03-02T21:01:09.727 回答
0

Laravel 护照仅与作为提供者的用户合作。即使您通过使用 PasswordGrant 和 UserRepository 添加上述更改来获取令牌,当您为 Post 和获取请求进行 API 调用时,也不能使用除用户以外的更改的护照提供程序。

如果供应商和客户必须需要,最好使用会话驱动程序创建多重身份验证。让“用户”模型仅适用于其表列支持管理员、API、供应商等的护照。

在这里回购laravel-multiAuth

于 2017-09-04T19:32:15.207 回答