Laravel Passport在前后端分离项目中的使用问题

George
  • 465

描述:


用户在前端登录以后,获取access_token,保存至localStorage,用户注销则销毁localStorage中的access_token。如果过期时间8小时的话,在access_token未过期的时间内,用户多次注销和登录,依然会产生多个access_token,且都是有效的!

问题:


  1. 如何合理的设置过期时间 ?

  2. 如何保证用户的同一Client_idaccess_token只有一个是有效的(未过期的)?

  3. 如何清理过期或被撤销的access_token ?

回复
阅读 11.3k
2 个回答
✓ 已被采纳

通过事件监听来清理token的方法:

将事件注册到 EventServiceProvider 中,代码如下:

protected $listen = [
    'App\Events\SomeEvent' => [
        'App\Listeners\EventListener',
    ],
    'Laravel\Passport\Events\AccessTokenCreated' => [
        'App\Listeners\Auth\RevokeOldTokens',
    ],
    'Laravel\Passport\Events\RefreshTokenCreated' => [
        'App\Listeners\Auth\PruneOldTokens',
    ],
];

然后创建两个事件的 Listener

  • RevokeOldTokens:

/**
     * Handle the event.
     *
     * @param  AccessTokenCreated  $event
     * @return void
     */
    public function handle(AccessTokenCreated $event)
    {
        Token::where('id', '!=', $event->tokenId)
            ->where('user_id', $event->userId)
            ->where('client_id', $event->clientId)
            ->where('expires_at', '<', Carbon::now())
            ->orWhere('revoked', true)
            ->delete();
    }
  • PruneOldTokens:

/**
     * Handle the event.
     *
     * @param  RefreshTokenCreated  $event
     * @return void
     */
    public function handle(RefreshTokenCreated $event)
    {
        DB::table('oauth_refresh_tokens')
            ->where('access_token_id', '!=', $event->accessTokenId)
            ->where('revoked', true)->delete();
    }

解决注销之后再登录的密码验证问题

因为前端登录后会将 refresh_tokensaccess_token保存到localStorage中,当用户注销登录以后,则将vuex state 中的状态销毁,仅保留 refresh_tokens。当用户下次登录时前端会检测refresh_tokens,如果存在则发起 refresh_tokens 请求,无需验证用户名密码。处于安全考虑即使存在refresh_tokens 也要验证用户名密码是否正确,于是就动手改了 LoginController

public function login(Request $request)
    {
        $credentials = $this->credentials($request);

        if ($this->guard('api')->attempt($credentials, $request->has('remember'))) {
            return $this->sendLoginResponse($request);
        }

        return \Response::json([
            'status' => "error",
            'message' => "用户名或密码有误"
        ], 401);
    }

    protected function sendLoginResponse(Request $request)
    {
        $this->clearLoginAttempts($request);

        return $this->authenticated($request);
    }

    protected function authenticated(Request $request)
    {
        return $this->authenticateClient($request);
    }

    protected function authenticateClient(Request $request)
    {
        $data = $request->all();
        if ($request->refresh_token) {
            $request->request->add([
                'grant_type' => $data['grant_type'],
                'client_id' => $data['client_id'],
                'client_secret' => $data['client_secret'],
                'refresh_token' => $data['refresh_token'],
                'scope' => ''
            ]);
        } else {
            $request->request->add([
                'grant_type' => $data['grant_type'],
                'client_id' => $data['client_id'],
                'client_secret' => $data['client_secret'],
                'username' => $data['staffid'],
                'password' => $data['password'],
                'scope' => ''
            ]);
        }

        $proxy = Request::create(
            'oauth/token',
            'POST'
        );

        $response = \Route::dispatch($proxy);
        $token = json_decode($response->getContent());
        $token->user = $request->user();

        return response()->json($token);
    }

    /**
     * Get the login username to be used by the controller.
     *
     * @return string
     */
    public function username()
    {
        return 'staffid';
    }

官方给出的方案是监听LaravelPassportEventsAccessTokenCreated事件来revoke多余的token
监听
LaravelPassportEventsRefreshTokenCreated事件来清除过期的token
在官方文档里面有,https://laravel.com/docs/5.4/...

你知道吗?

宣传栏