【问题标题】:Multi Auth with Laravel 5.4 and Passport使用 Laravel 5.4 和 Passport 进行多重身份验证
【发布时间】:2017-07-08 22:59:53
【问题描述】:

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

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

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

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

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

【问题讨论】:

    标签: php laravel oauth-2.0 laravel-passport


    【解决方案1】:

    如果你还需要的话。

    我更喜欢使用角色,有一个很棒的插件: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:对不起,我的英语不好。

    【讨论】:

      【解决方案2】:

      你必须修改主库文件。

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

      我希望这会有所帮助。

      【讨论】:

        【解决方案3】:

        我为这个问题创建了一个小包。这是完整文档的链接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()-&gt;guard('customers')-&gt;user()

        【讨论】:

          【解决方案4】:

          我已经在 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
          

          您还可以通过将provider 列值users 替换为doctors,在oauth_clients 表中手动创建用户

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

          【讨论】:

          • 我按照您的所有步骤操作,但我收到此错误个人访问客户端未找到。请创建一个
          【解决方案5】:

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

          【讨论】:

            【解决方案6】:

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

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

            在这里回购laravel-multiAuth

            【讨论】:

              【解决方案7】:

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

              【讨论】:

                猜你喜欢
                • 2020-12-30
                • 2019-12-20
                • 1970-01-01
                • 2017-11-15
                • 2019-01-31
                • 2019-03-21
                • 2021-02-08
                • 2018-05-07
                • 2017-10-07
                相关资源
                最近更新 更多