passport API 认证 -- 多表登录

passport 多表登录

添加一个 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
本帖由系统于 5年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 14

@dptms 博客:lucms - vue 与 Laravel 开发的后台管理系统 这里可以测试,这个就是基于 jwt 的,不是 session

5年前 评论
ThinkQ

很不错额。

5年前 评论

老哥 我现在前后台都获取到token,但是现在前台获取的token通过api请求后台的info接口,也能获取到数据 :joy:,后台的token请求前台的也是一样

4年前 评论
liumosheng 4年前
raytol 4年前

刷新 token 时未指定用户表

4年前 评论
wonbin

看了网上的一些解决方案 这篇最符合我口味,完美 :smile:

4年前 评论
Ucer (楼主) 4年前
JasonLi9168

你好, 我有个 laravel passport UUID user_id 问题,网上搜索了几天,还是没有头绪, 希望大家可以帮忙指点一二吗, 在这里先谢过了。

4年前 评论

这个太复杂了点,不用这么麻烦的吧!两个表,用中间件区分不就行了!还用什么监听事件!这个搞的有点大!

4年前 评论
TommyTu

@Ucer 请问下 laravel8 没有找到这个类,你是怎么处理的呀,Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory

3年前 评论

@TommyTu

composer require laminas/laminas-diactoros
use Laminas\Diactoros\StreamFactory;
use Laminas\Diactoros\ResponseFactory;
use Laminas\Diactoros\UploadedFileFactory;
use Laminas\Diactoros\ServerRequestFactory;

$psr = (new PsrHttpFactory(
            new ServerRequestFactory,
            new StreamFactory,
            new UploadedFileFactory,
            new ResponseFactory))->createRequest($request);
2年前 评论

支持单点登录吗

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!