使用Laravel 5.4和Passport进行Multi Auth

时间:2017-02-19 18:57:28

标签: php laravel oauth-2.0 laravel-passport

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

我有3个auth警卫/提供者设置,总共4个。 用户,供应商,管理员和API

其中2个Auths需要访问护照,因此每个用户都需要能够发放令牌。但Passport会自动获取API身份验证提供程序,但我希望根据登录的用户进行更改。如果用户执行用户提供程序,如果是供应商,那么供应商提供商

但Passport目前仅支持1种用户类型,因此默认为 API提供程序

这有什么更好的吗?或者我应该使用基于角色的身份验证。

7 个答案:

答案 0 :(得分:5)

如果你还需要。

我更喜欢使用角色,有一个惊人的插件:https://github.com/larapacks/authorization

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

对于多重防护,你必须覆盖一些代码。

您可以创建自己的PassportServiceProvider,而不是加载PassportServiceProvider,并扩展PassportServiceProvider并覆盖方法makePasswordGrant。 在此方法上,您将更改自己的存储库扩展的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;
    }

}

UserRepository

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:对不起,我的英文不好。

答案 1 :(得分:3)

您必须修改主库文件。

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

查找 getUserEntityByUserCredentials 并复制完整方法,然后使用名称 getEntityByUserCredentials粘贴此方法。不要修改主要功能,因为它在某处使用。

//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',
  ],

我希望这会有所帮助。

答案 2 :(得分:1)

我为此问题创建了一个小包。这是完整文档link

的链接

但要点是,只要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()

答案 3 :(得分:1)

我们正在等待Laravel将此功能添加到其程序包中,但是对于那些想要添加此功能的人,我建议使用this程序包

答案 4 :(得分:1)

我已经在 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模型(User)修改我的App/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列值provider替换为usersdoctors表中手动创建用户

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

答案 5 :(得分:0)

Laravel护照仅与用户一起提供。即使您通过使用PasswordGrant&amp;添加上述更改来获取令牌UserRepository,当您为Post发送API调用并获取请求时,不会使用除User之外的已更改的护照提供程序。

如果必须作为供应商和客户需要,最好使用会话驱动程序创建多个auth。让用户&#39;仅适用于表列支持admin,API,vendor等的护照的模型。

回复laravel-multiAuth

答案 6 :(得分:0)

如果您正在寻找 Passport Multi-Auth API 的解决方案 我建议您this solution