passport API 認證 -- 多表登入

Ucer發表於2018-07-02

新增一個 guard

config/auth.php

<?php
.
.
.
 'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'passport',
            'provider' => 'users',
        ],
        'admin_user_api' => [
            'driver' => 'passport',
            'provider' => 'admin_users',
        ],
    ],
  .
  .
  .
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
        'admin_users' => [
            'driver' => 'eloquent',
            'model' => App\Models\AdminUser::class,
        ],
  .
  .
  .
    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
        ],
        'admin_users' => [
            'provider' => 'admin_users',
            'table' => 'password_resets',
            'expire' => 60,
        ],
    ],

  ];

?>

建立模型

/app/Models/AdminUser.php

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;
use DB;

class AdminUser extends Authenticatable
{
    use  HasApiTokens;

}
?>

建立 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);
    }
}
?>

註冊路由中介軟體

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
  .
  .
  .
    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,
    ];
  .
  .
  .
  ?>

生成 passport 路由

<?php

namespace App\Providers;

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

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();
        Passport::routes(function (RouteRegistrar $router) {
            $router->forAccessTokens();
        }, ['prefix' => 'api/oauth', 'middleware' => 'passport-administrators']);

        Passport::tokensExpireIn(now()->addDay(3));
        Passport::refreshTokensExpireIn(now()->addDay(3));

    }
}
?>

生成資料遷移檔案

<?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');
    }
}

?>

新增一個事件監聽器

<?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 = [
        'App\Events\Event' => [
            'App\Listeners\EventListener',
        ],
        'Laravel\Passport\Events\AccessTokenCreated' => [
            'App\Listeners\PassportAccessTokenCreated',
        ],
    ];

    /**
     * Register any events for your application.
     *
     * @return void
     */
    public function boot()
    {
        parent::boot();

        //
    }
}

?>
<?php

namespace App\Listeners;

use Carbon\Carbon;
use Illuminate\Support\Facades\DB;

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(),
        ]);
    }
}
?>

新增一個全域性中介軟體來處理請求

<?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);
    }
}
?>
<?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\TrustProxies::class,
        \App\Http\Middleware\PassportCustomProviderAccessToken::class
    ];
  .
  .
  .
  ?>

登入登出流程

<?php
Route::namespace('Api')->group(function () {

    Route::post('login', 'LoginController@login');
    Route::post('logout', 'LoginController@logout');
    Route::post('refreshtoken', 'LoginController@refreshToken');

    // 多表登入測試
    Route::post('admin_user/login', 'LoginController@adminUserLogin');
    Route::get('admin_user', 'AdminUsersController@index');
    Route::post('admin_user/logout', 'LoginController@adminUserLogout');

});
?>
<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Api\Traits\ProxyTrait;
use App\Models\AdminUser;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;

class LoginController extends ApiController
{
    use AuthenticatesUsers, ProxyTrait;

    public function __construct()
    {
        $this->middleware('guest')->except('logout,adminUserLogout,refreshToken');
    }
.
.
.
    public function adminUserLogin(Request $request)
    {

        $admin_user = AdminUser::where('email', $request->email)
            ->firstOrFail();

        if (!Hash::check($request->password, $admin_user->password)) {
            return $this->failed('密碼不正確');
        }

        $admin_user->last_login_at = Carbon::now();
        $admin_user->save();

        $tokens = $this->authenticate('admin_users');
        return $this->success(['token' => $tokens, 'user' => $admin_user]);
    }

    public function adminUserLogout()
    {
        if (\Auth::guard('admin_user_api')->check()) {
//            \Auth::guard('admin_user_api')->user()->token()->revoke();
            \Auth::guard('admin_user_api')->user()->token()->delete();
        }
  .
  .
  .
  ?>

ProxyTrait

<?php

namespace App\Http\Controllers\Api\Traits;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

trait ProxyTrait
{
    public function authenticate($guard = '')
    {
        $client = new Client();

        try {
            $url = request()->root() . '/api/oauth/token';

            if ($guard) {
                $params = array_merge(config('passport.proxy'), [
                    'username' => request('email'),
                    'password' => request('password'),
                    'provider' => $guard
                ]);
            } else {
                $params = array_merge(config('passport.proxy'), [
                    'username' => request('email'),
                    'password' => request('password'),
                ]);
            }

            $respond = $client->request('POST', $url, ['form_params' => $params]);
        } catch (RequestException $exception) {
            abort(401, '請求失敗,伺服器錯誤');
        }

        if ($respond->getStatusCode() !== 401) {
            return json_decode($respond->getBody()->getContents(), true);
        }
        abort(401, '賬號或密碼錯誤');

    }

    public function getRefreshtoken()
    {
        $client = new Client();

        try {
            $url = request()->root() . '/api/oauth/token';

            $params = array_merge(config('passport.refresh_token'), [
                'refresh_token' => request('refresh_token'),
            ]);

            $respond = $client->request('POST', $url, ['form_params' => $params]);
        } catch (RequestException $exception) {
            abort(401, '請求失敗,伺服器錯誤');
        }

        if ($respond->getStatusCode() !== 401) {
            return json_decode($respond->getBody(), true);
        }
        abort(401, '不正確的 refresh_token');

    }
}
<?php

namespace App\Http\Controllers\Admin;

use Illuminate\Http\Request;

class AdminUsersController extends AdminController
{
    public function __construct()
    {
        parent::__construct();
        $this->middleware('passport-administrators');
    }

    public function index(Request $request)
    {
        dd($request->user('admin_user_api'));
        //dd(\Auth::guard('admin_user_api')->id());
    }
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結
do it now

相關文章