Auth::logoutOtherDevices 导致密码错误问题

注:经 [@ALMAS](https://learnku.com/users/7036) 提醒,发现user模型中有定义一个修改器,用于密码处理,问题出在该处,并非laravel 问题。特此纠正,感谢 [@ALMAS](https://learnku.com/users/7036) 的提醒。
代码如下:

 public function setPasswordAttribute($value)
    {
        $value = str_replace(' ', '', $value);
        $this->attributes['password'] = bcrypt($value);
    }

以上纠正于:2019年7月16日 20:32分!

最近做开发的时候,要求实现单设备登录功能,看了下文档,发现 Auth::logoutOtherDevices($password) 正好满足条件。于是兴冲冲的使用了该方法。结果悲剧了, 账号登录后,Auth::logoutOtherDevices 处理完,再想登录的时候,报密码错误。找问题发现使用 Auth::logoutOtherDevices 后,数据库中的密码变了,用 password_verify 去验证数据库中改变后的密码,死活验证不通过。 好嘛,只能跟踪源码找问题了。

     /**
     * Invalidate other sessions for the current user.
     *
     * The application must be using the AuthenticateSession middleware.
     *
     * @param  string  $password
     * @param  string  $attribute
     * @return bool|null
     */
    public function logoutOtherDevices($password, $attribute = 'password')
    {
        if (! $this->user()) {
            return;
        }

        $result = tap($this->user()->forceFill([
          **$attribute => Hash::make($password),** 
        ]))->save();

        if ($this->recaller() ||
            $this->getCookieJar()->hasQueued($this->getRecallerName())) {
            $this->queueRecallerCookie($this->user());
        }

        $this->fireOtherDeviceLogoutEvent($this->user());

        return $result;
    }

在该方法中,发现密码是Hash::make加密后存储。刚开始以为是这里加密方式问题,后来经测试发现不是。真正的原因是密码被加密了两次,$attribute => Hash::make($password) 加密了一次,在入库的时候又做了一次hash,最终导致密码验证不通过。找到问题就简单了,直接改成如下代码,问题解决。

        $result = tap($this->user()->forceFill([
            **$attribute => $password,**
        ]))->save();

初次发文,请多关照!

本作品采用《CC 协议》,转载必须注明作者和本文链接
longren610
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 12

Laravel多少版本有这个功能?

4年前 评论
longren610 (楼主) 4年前
ALMAS

话说入库时怎么会再hash一次?是不是自己写了修改器?

4年前 评论
longren610 (楼主) 4年前
ALMAS (作者) 4年前
longren610 (楼主) 4年前

直接改源码有点不妥吧,可以复写这个方法吗?

4年前 评论
longren610 (楼主) 4年前
captain2021 (作者) 4年前
longren610 (楼主) 4年前
captain2021 (作者) 4年前
Epona

此方法要求用户提供其当前密码,你的应用程序应通过输入表单接受该密码:

应该把未加密的密码传进去吧

4年前 评论
longren610 (楼主) 4年前

**这是什么语法

[
          **$attribute => Hash::make($password),** 
        ]
4年前 评论
longren610

@lovecn 前后的**号,只是想做一个提示,不是语法。。。 :joy:

4年前 评论

@longren610 大佬,这个forceFill是个静态方法吗?没找到源码在哪里啊

4年前 评论
longren610

@nullable 我也只是一只刚接触laravel不久的菜鸟 :joy: 不是大佬啦。如果是laravel5.8的话,这个forceFill 在 vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php 里面,第347行

4年前 评论
nullable 4年前

我用的是自带的Auth,只要在LoginController里重写authenticated方法就可以了

    protected function authenticated(Request $request, $user)
    {
        $this->guard()->logoutOtherDevices($request->password);
    }

然后我又碰到了一个问题:在使用

Auth::loginUsingId(1,  $remember  =  false);  //登录指定用户 ID 的用户到应用上

登录时,怎么退出其它设备的登录状态?

4年前 评论
longren610 (楼主) 4年前
hiscxy (作者) 4年前
longren610 (楼主) 4年前

app接口不能用这个嘛

3年前 评论
longren610

@test-1 不能,app验证用的是token,这个其实是每次登录,将密码重新计算一次hash值,存入用户表中,web登录状态检测到用户表中密码字段值变了,就会把用户踢下线。而app登录状态一般都是用token,要实现其实也简单,在用户登录的时候同时修改一下app用的token就行了。

3年前 评论

我也遇到了这个问题,我传了未加密的密码进去,我同时登录两台设备,数据库密码还是别改了。 Auth::guard('money')->logoutOtherDevices($validatedData['password']);

3年前 评论
longren610 (楼主) 3年前
xiayimiaozhongjianni (作者) 3年前
longren610 (楼主) 3年前

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