翻译:Laravel 5.3-5.4 Passport包 多认证(Guard)操作

原文地址:https://github.com/laravel/passport/issues/161

这里我觉得renanwilliam的步骤比较详细,故只翻译他的回复:

  1. 添加一个新的用户提供者到 config/auth.php,并添加Authenticatable类扩展到模型,并使用HasRolesHasApiTokens的traits
    <?php

    return [

        /*
        |--------------------------------------------------------------------------
        | Authentication Defaults
        |--------------------------------------------------------------------------
        |
        | This option controls the default authentication "guard" and password
        | reset options for your application. You may change these defaults
        | as required, but they're a perfect start for most applications.
        |
        */

        'defaults' => [
            'guard' => 'web',
            'passwords' => 'users',
        ],

        /*
        |--------------------------------------------------------------------------
        | Authentication Guards
        |--------------------------------------------------------------------------
        |
        | Next, you may define every authentication guard for your application.
        | Of course, a great default configuration has been defined for you
        | here which uses session storage and the Eloquent user provider.
        |
        | All authentication drivers have a user provider. This defines how the
        | users are actually retrieved out of your database or other storage
        | mechanisms used by this application to persist your user's data.
        |
        | Supported: "session", "token"
        |
        */

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

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

        /*
        |--------------------------------------------------------------------------
        | User Providers
        |--------------------------------------------------------------------------
        |
        | All authentication drivers have a user provider. This defines how the
        | users are actually retrieved out of your database or other storage
        | mechanisms used by this application to persist your user's data.
        |
        | If you have multiple user tables or models you may configure multiple
        | sources which represent each model / table. These sources may then
        | be assigned to any extra authentication guards you have defined.
        |
        | Supported: "database", "eloquent"
        |
        */

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

            'admins' => [
                'driver' => 'eloquent',
                'model' => App\Administrator::class,
            ],

            // 'users' => [
            //     'driver' => 'database',
            //     'table' => 'users',
            // ],
        ],

        /*
        |--------------------------------------------------------------------------
        | Resetting Passwords
        |--------------------------------------------------------------------------
        |
        | You may specify multiple password reset configurations if you have more
        | than one user table or model in the application and you want to have
        | separate password reset settings based on the specific user types.
        |
        | The expire time is the number of minutes that the reset token should be
        | considered valid. This security feature keeps tokens short-lived so
        | they have less time to be guessed. You may change this as needed.
        |
        */

        'passwords' => [
            'users' => [
                'provider' => 'users',
                'table' => 'password_resets',
                'expire' => 60,
            ],

            'admins' => [
                'provider' => 'admins',
                'table' => 'password_resets',
                'expire' => 60,
            ],
        ],

    ];
  1. 像这样创建一个新的中间件PassportCustomProvider
    <?php

    namespace App\Http\Middleware;

    use Closure;
    use Illuminate\Support\Facades\Config;

    class PassportCustomProvider
    {
        /**
         * Handle an incoming request.
         *
         * @param  \Illuminate\Http\Request $request
         * @param  \Closure $next
         * @return mixed
         */
        public function handle($request, Closure $next)
        {
            $params = $request->all();
            if (array_key_exists('provider', $params)) {
                Config::set('auth.guards.api.provider', $params['provider']);
            }
            return $next($request);
        }
    }
  1. 注册中间件
    <?php

    namespace App\Http;

    use Illuminate\Foundation\Http\Kernel as HttpKernel;

    class Kernel extends HttpKernel
    {
        /**
         * The application's global HTTP middleware stack.
         *
         * These middleware are run during every request to your application.
         *
         * @var array
         */
        protected $middleware = [
            \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
            \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
            \App\Http\Middleware\TrimStrings::class,
            \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        ];

        /**
         * The application's route middleware groups.
         *
         * @var array
         */
        protected $middlewareGroups = [
            'web' => [
                \App\Http\Middleware\EncryptCookies::class,
                \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
                \Illuminate\Session\Middleware\StartSession::class,
                // \Illuminate\Session\Middleware\AuthenticateSession::class,
                \Illuminate\View\Middleware\ShareErrorsFromSession::class,
                \App\Http\Middleware\VerifyCsrfToken::class,
                \Illuminate\Routing\Middleware\SubstituteBindings::class,
            ],

            'api' => [
                'throttle:60,1',
                'bindings',
            ],
        ];

        /**
         * The application's route middleware.
         *
         * These middleware may be assigned to groups or used individually.
         *
         * @var array
         */
        protected $routeMiddleware = [
            'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
            'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
            'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
            'can' => \Illuminate\Auth\Middleware\Authorize::class,
            'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
            'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
            'passport-administrators' => \App\Http\Middleware\PassportCustomProvider::class,
        ];
    }
  1. 带着中间件在AuthServiceProvider封装认证路由
    <?php

    namespace App\Providers;

    use Illuminate\Support\Facades\Route;
    use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
    use Laravel\Passport\Passport;

    class AuthServiceProvider extends ServiceProvider
    {
        /**
         * The policy mappings for the application.
         *
         * @var array
         */
        protected $policies = [
            'App\Model' => 'App\Policies\ModelPolicy',
        ];

        /**
         * Register any authentication / authorization services.
         *
         * @return void
         */
        public function boot()
        {
            $this->registerPolicies();

            Route::group(['middleware' => 'passport-administrators'], function () {
                Passport::routes();
            });
        }

    }
  1. 在你的/oauth/token请求里添加提供者provider变量
    POST /oauth/token HTTP/1.1
    Host: localhost
    Accept: application/json, text/plain, */*
    Content-Type: application/json;charset=UTF-8
    Cache-Control: no-cache

    {
    	"username":"user@domain.com",
    	"password":"password",
    	"grant_type" : "password",
    	"client_id": "client-id",
    	"client_secret" : "client-secret",
    	"provider" : "admins"
    }

The problem after this step is the token saved in oauth_access_tokens only contains the administrator ID. When we use the token received to authenticate, it not look at Administrator models.

在这个步骤后的问题是, 管理员ID只包含在oauth_access_tokens的token中。当我们使用这个TOKEN进行认证时,不用在意管理员的模型。

为了使它成为Bearer令牌,我们必须做以下步骤

  1. 创建一个迁移保存 访问token 和提供者之间的关系:
    <?php

    use Illuminate\Support\Facades\Schema;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Database\Migrations\Migration;

    class OauthAccessTokenProviders extends Migration
    {
        /**
         * Run the migrations.
         *
         * @return void
         */
        public function up()
        {
            Schema::create('oauth_access_token_providers', function (Blueprint $table) {
                $table->string('oauth_access_token_id', 100)->primary();
                $table->string('provider');
                $table->timestamps();

                $table->foreign('oauth_access_token_id')
                    ->references('id')->on('oauth_access_tokens')
                    ->onDelete('cascade');
            });
        }

        /**
         * Reverse the migrations.
         *
         * @return void
         */
        public function down()
        {
            Schema::drop('oauth_access_token_providers');
        }
    }
  1. 在EventServiceProvider添加一个监听事件
    <?php

    namespace App\Providers;

    use Illuminate\Support\Facades\Event;
    use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

    class EventServiceProvider extends ServiceProvider
    {
        /**
         * The event listener mappings for the application.
         *
         * @var array
         */
        protected $listen = [
            'Laravel\Passport\Events\AccessTokenCreated' => [
                'App\Listeners\PassportAccessTokenCreated',
            ],
        ];

        /**
         * Register any events for your application.
         *
         * @return void
         */
        public function boot()
        {
            parent::boot();
        }
    }
  1. 添加以下代码到你的监听器上,以保存访问TOKEN和提供者之间的关系
    <?php

    namespace App\Listeners;

    use App\Events\Laravel\Passport\Events\AccessTokenCreated;
    use Carbon\Carbon;
    use Illuminate\Queue\InteractsWithQueue;
    use Illuminate\Contracts\Queue\ShouldQueue;
    use Illuminate\Support\Facades\DB;
    use Illuminate\Support\Facades\Log;

    class PassportAccessTokenCreated
    {
        /**
         * Create the event listener.
         *
         * @return void
         */
        public function __construct()
        {
            //
        }

        /**
         * Handle the event.
         *
         * @param  \Laravel\Passport\Events\AccessTokenCreated $event
         * @return void
         */
        public function handle(\Laravel\Passport\Events\AccessTokenCreated $event)
        {
            $provider = \Config::get('auth.guards.api.provider');
            DB::table('oauth_access_token_providers')->insert([
                "oauth_access_token_id" => $event->tokenId,
                "provider" => $provider,
                "created_at" => new Carbon(),
                "updated_at" => new Carbon(),
            ]);
        }
    }
  1. 创建一个新的全局中间件来处理每个请求对自定义提供者的设置
    <?php

    namespace App\Http\Middleware;

    use Closure;
    use Illuminate\Support\Facades\DB;
    use League\OAuth2\Server\ResourceServer;
    use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;

    class PassportCustomProviderAccessToken
    {

        private $server;

        public function __construct(ResourceServer $server)
        {
            $this->server = $server;
        }

        /**
         * Handle an incoming request.
         *
         * @param  \Illuminate\Http\Request $request
         * @param  \Closure $next
         * @return mixed
         */
        public function handle($request, Closure $next)
        {
            $psr = (new DiactorosFactory)->createRequest($request);

            try {
                $psr = $this->server->validateAuthenticatedRequest($psr);
                $token_id = $psr->getAttribute('oauth_access_token_id');
                if ($token_id) {
                    $access_token = DB::table('oauth_access_token_providers')->where('oauth_access_token_id',
                        $token_id)->first();

                    if ($access_token) {
                        \Config::set('auth.guards.api.provider', $access_token->provider);
                    }
                }
            } catch (\Exception $e) {

            }

            return $next($request);
        }
    }
  1. 最后,在app/Http/Kernel的全局http中间件注册你的中间件
    <?php

    namespace App\Http;

    use Illuminate\Foundation\Http\Kernel as HttpKernel;

    class Kernel extends HttpKernel
    {
        /**
         * The application's global HTTP middleware stack.
         *
         * These middleware are run during every request to your application.
         *
         * @var array
         */
        protected $middleware = [
            \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
            \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
            \App\Http\Middleware\TrimStrings::class,
            \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
            \App\Http\Middleware\PassportCustomProviderAccessToken::class
        ];

        /**
         * The application's route middleware groups.
         *
         * @var array
         */
        protected $middlewareGroups = [
            'web' => [
                \App\Http\Middleware\EncryptCookies::class,
                \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
                \Illuminate\Session\Middleware\StartSession::class,
                // \Illuminate\Session\Middleware\AuthenticateSession::class,
                \Illuminate\View\Middleware\ShareErrorsFromSession::class,
                \App\Http\Middleware\VerifyCsrfToken::class,
                \Illuminate\Routing\Middleware\SubstituteBindings::class,
            ],

            'api' => [
                'throttle:60,1',
                'bindings',
            ],
        ];

        /**
         * The application's route middleware.
         *
         * These middleware may be assigned to groups or used individually.
         *
         * @var array
         */
        protected $routeMiddleware = [
            'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
            'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
            'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
            'can' => \Illuminate\Auth\Middleware\Authorize::class,
            'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
            'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
            'passport-custom-provider' => \App\Http\Middleware\PassportCustomProvider::class
        ];
    }

现在,它只是缺少刷新token功能和处理cookie登录而已。