首页 > 解决方案 > “logoutOtherDevices”不适用于 Laravel Sanctum

问题描述

更改密码后,我想Auth::logoutOtherDevices($currentPassword)在 laravel 8 中使用从其他设备注销用户。根据文档,我取消了该行的注释\Illuminate\Session\Middleware\AuthenticateSession::class,。但不幸的是,它不起作用。它说Method Illuminate\Auth\RequestGuard::viaRemember does not exist.任何人都可以帮我解决一下如何在Auth::logoutOtherDevices($currentPassword)任何手动方法的帮助下从其他设备功能中添加注销?

这是我的代码:

$rules = [
        'currentpass' => 'required',
        'newpass'     => 'required|min:6',
        'confnewpass' => 'required|same:newpass|min:6'
    ];

    $messages = [
        'currentpass.required' => 'Please enter your current password.',
        'newpass.required'     => 'Please provide a new password.',
        'newpass.min'          => 'Password must contain minimum 6 characters.',
        'confnewpass.required' => 'Please provide your new password again to confirm.',
        'confnewpass.same'     => 'Both new passwords must be same.',
        'confnewpass.min'      => 'Password must contain minimum 6 characters.'
    ];

    $this->validate($request, $rules, $messages);

    $currentPass = $request->input('currentpass');
    $newPass     = $request->input('newpass');

    try {
        $user = User::findOrFail(Auth::id());
    } catch (ModelNotFoundException $ex) {
        $response['error'] = true;
        $response['errors']['notFound'] = ['User Not Found.'];

        return response()
            ->json($response, 400, [], JSON_PRETTY_PRINT);
    }

    if (!Hash::check($currentPass, $user->password)) {
        return Redirect::back()
            ->withErrors(['Current Password', 'Please provide your current password properly.']);
    }

    $isChar = preg_match('/[a-zA-Z]+/', $newPass);
    $isNum  = preg_match('/\d+/', $newPass);

    if (!($isChar && $isNum)) {
        $response['error'] = 'Password must contain minimum 6 characters with at-least one letter and one number.';

        return response()
            ->json($response, 200, [], JSON_PRETTY_PRINT);
    }

    /** hash password */
    $hashpass = Hash::make($newPass);

    $user->password = $hashpass;

    try {
        $user->save();
    } catch (QueryException $ex) {
        return Redirect::back()
            ->withErrors(['query', $ex->getMessage()]);
    }

    Auth::logoutOtherDevices($currentPass);

    return Redirect::back()
        ->with('success', 'Your password has been successfully updated.');

标签: phpauthenticationlaravel-8laravel-sanctum

解决方案


这是因为 Laravel Sanctum 使用了自己的中间件,即Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful. 虽然Auth::logoutOtherDevices($currentPassword)需要\Illuminate\Session\Middleware\AuthenticateSession工作。

要解决此问题,您可以扩展\Illuminate\Session\Middleware\AuthenticateSession到使用Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful. 以下是步骤:

  1. AuthenticateApiSession.phpapp/Http/Middleware(或您喜欢的任何其他文件夹)中创建一个新文件。AuthenticateApiSession.php应该是这样的:

    <?php
    
    namespace App\Http\Middleware;
    
    use Closure;
    use Illuminate\Session\Middleware\AuthenticateSession;
    
    class AuthenticateApiSession extends AuthenticateSession
    {
        /**
         * Handle an incoming request.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  \Closure  $next
         * @return mixed
         */
        public function handle($request, Closure $next)
        {
            if (! $request->hasSession() || ! $request->user()) {
                return $next($request);
            }
    
            // Remove or comment this code block, or you'll get error.
            /*if ($this->auth->viaRemember()) {
                $passwordHash = explode('|', $request->cookies->get($this->auth->getRecallerName()))[2] ?? null;
    
                if (! $passwordHash || $passwordHash != $request->user()->getAuthPassword()) {
                    $this->logout($request);
                }
            }*/
    
            if (! $request->session()->has('password_hash')) {
                $this->storePasswordHashInSession($request);
            }
    
            if ($request->session()->get('password_hash') !== $request->user()->getAuthPassword()) {
                $this->logout($request);
            }
    
            return tap($next($request), function () use ($request) {
                $this->storePasswordHashInSession($request);
            });
        }
    }
    
  2. 将新创建的App\http\Middleware\AuthenticateApiSession类添加apiapp/Http/Kernel.php. 请注意,它必须在 之后插入EnsureFrontendRequestsAreStateful

     'api' => [
         EnsureFrontendRequestsAreStateful::class,
         \App\Http\Middleware\AuthenticateApiSession::class,
         'throttle:240,1',
         'auth:airlock', 
         'branch.default', 
         'bindings',
     ],
    

就是这样,现在从其他设备对受保护的 API 端点的每个 ajax 请求都应该在调用401后返回响应。Auth::logoutOtherDevices($currentPassword)您应该设置您的 SPA(您正在使用 sanctum,所以我假设您正在构建一个 SPA)以处理 ajax 请求中的无效会话。401遇到ajax 响应时,可能会使用 javascript 将用户重定向到登录页面。


推荐阅读