User ID not found in the token登录出错怎么解决?

有用户提醒我,说在网站登录的时候,会提示 User ID not found in the token ,第一反应是B2主题更新到2.5.1的问题,恰好我本地还有之前版本的备份,覆盖恢复之前2.4.x版本测试一下,可问题居然还存在,这就有点意思了。

开始调试找错

先尝试一下,手机号码、用户名、社交快捷登录都不会报错,唯有邮箱登录,且密码错误时才会报错,知道出错的状况之后,我们开始针对性的找代码。

从登陆入手

既然是登陆出错,当然先从登陆开始,我们先找到文件:/b2/Modules/Common/Login.php 仔细找了一下,发现除了 check_login_name() 和 bind_user_login() 似乎没有什么登录相关的函数。

我们再回头登录,按F12进入调试模式,抓包登录请求看看是提交到哪个文件:

/wp-json/jwt-auth/v1/token

JWT插件

从图片上可以看到,登录是提交到jwt的,那么我们找到到对应jwt插件即可,文件:b2/Library/jwt/public/class-jwt-auth-public.php

    public function add_api_routes()
    {
        register_rest_route($this->namespace, 'token', array(
            'methods' => 'POST',
            'callback' => array($this, 'generate_token'),
        ));

        register_rest_route($this->namespace, 'token/validate', array(
            'methods' => 'POST',
            'callback' => array($this, 'validate_token'),
        ));
    }

从代码不难看出,add_api_routes()函数定义了一个/token路由,对应的callback是generate_token(),那么我们再看一下这里的代码:

generate_token()

    public function generate_token($request)
    {
        $secret_key = defined('JWT_AUTH_SECRET_KEY') ? JWT_AUTH_SECRET_KEY : false;
        $username = $request->get_param('username');
        $password = $request->get_param('password');

        /** First thing, check the secret key if not exist return a error*/
        if (!$secret_key) {
            return new WP_Error(
                'jwt_auth_bad_config',
                __('JWT is not configurated properly, please contact the admin', 'wp-api-jwt-auth'),
                array(
                    'status' => 403,
                )
            );
        }
        /** Try to authenticate the user with the passed credentials*/
        $user = wp_authenticate($username, $password);

        /** If the authentication fails return a error*/
        if (is_wp_error($user)) {
            $error_code = $user->get_error_code();
            return new WP_Error(
                '[jwt_auth] ' . $error_code,
                $user->get_error_message($error_code),
                array(
                    'status' => 403,
                )
            );
        }

        /** Valid credentials, the user exists create the according Token */
        $issuedAt = time();
        $notBefore = apply_filters('jwt_auth_not_before', $issuedAt, $issuedAt);
        $expire = apply_filters('jwt_auth_expire', $issuedAt + (DAY_IN_SECONDS * 7), $issuedAt);

        $token = array(
            'iss' => get_bloginfo('url'),
            'iat' => $issuedAt,
            'nbf' => $notBefore,
            'exp' => $expire,
            'data' => array(
                'user' => array(
                    'id' => $user->data->ID,
                ),
            ),
        );

        /** Let the user modify the token data before the sign. */
        $token = JWT::encode(apply_filters('jwt_auth_token_before_sign', $token, $user), $secret_key);

        /** The token is signed, now create the object with no sensible user data to the client*/
        $data = array(
            'token' => $token,
            'user_email' => $user->data->user_email,
            'user_nicename' => $user->data->user_nicename,
            'user_display_name' => $user->data->display_name,
        );

        /** Let the user modify the data before send it back */
        return apply_filters('jwt_auth_token_before_dispatch', $data, $user);
    }

简单看一下,这个函数大致功能是根据前台提交的username和password,通过 wp_authenticate() 函数进行验证账号密码是否错误,然后返回 WP_User 对象或者 WP_Error 错误。

这里写的代码看上去没有什么问题,但是我们仔细想一下,之前登陆的报错提示 User ID not found in the token,这里好像没有。

我们再尝试搜索一下,在当前文件找到了:

validate_token()

    public function validate_token($output = true)
...
            if (!isset($token->data->user->id)) {
                /** No user id in the token, abort!! */
                return new WP_Error(
                    'jwt_auth_bad_request',
                    'User ID not found in the token',
                    array(
                        'status' => 403,
                    )
                );
            }
...

从函数名称上面不难看出 validate_token() 是用来验证token的方法,理论上不应该出现在登录时返回的报错提示,因为他是登陆之后,用来验证token的函数。

我们再次回过头,再仔细看一下登录时抓包返回的response:

response

刚刚没注意,状态返回码是200,并且返回了token,这个就等于是说登录成功了?这个问题就大了,要知道我们输入的是错误的密码啊!

错密码也返回token

我们再仔细看一下 generate_token() 这个函数:

$user = wp_authenticate($username, $password);

        /** If the authentication fails return a error*/
        if (is_wp_error($user)) {
            $error_code = $user->get_error_code();
            return new WP_Error(
                '[jwt_auth] ' . $error_code,
                $user->get_error_message($error_code),
                array(
                    'status' => 403,
                )
            );
        }

可以确定这个写法是没有问题的,wp_authenticate 也的的确确是用来验证账号密码的 WordPress函数,按照正常情况,输入错误的密码,会返回一个 [jwt_auth] xxx错误代码的提示,但是目前居然没有返回错误,而是登陆成功,为什么会出现这样的情况呢?

我们尝试在 wp_authenticate() 后面追加一行 var_dump($user) 我们看下到底发生了什么情况!

[“error”]=> string(12) “非法操作”

疑似WP的BUG

咦,返回一个错误提示,这是什么情况,也就是说其实没有登录成功的,但是通过了 is_wp_error() 判断,难道这是一个WP的BUG?

我尝试百度+谷歌找了一圈没有发现有相关的答案,也没有人遇到类似的问题,这就奇怪了jwt都是一个通用的插件,按道理不可能出现这样低级的bug,影响可是百万级的,既然想不通,我们就先尝试自己解决这个问题吧。

按照惯例,我们先找到js中对应的登录方法,全局搜索/token,我们很快就找到对应的代码:

开始修复

抱歉,隐藏内容须成功 评论本文 刷新 可见!
但是这样修改父主题的js,以后每次更新都要修改,很麻烦,我们有没有更好的办法呢?有的。

更好的办法

隐藏内容,您需要满足以下条件方可查看
End
你可以在子主题的function.php追加上面的代码,也可以修正登录的BUG,而且以后不影响B2父主题的更新。

人已赞赏
二次开发

定制功能演示:VIP视频

2020-10-3 0:34:28

二次开发

B2主题如何批量修改文章隐藏内容阅读权限?

2020-10-15 21:44:52

免责声明本文中提到的资源均来自于互联网,仅供个人学习交流,若您喜欢本文可附上原文链接随意转载。
版权人申诉:我要申诉
14 条回复 A文章作者 M管理员
  1. 看一看看一看

  2. 感谢分享!!!

  3. fuck吊个

  4. 感谢分享!!!

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索