探讨 `wp_get_current_user()` 函数的源码,它是如何获取当前登录用户的 `WP_User` 对象的?

咳咳,麦克风测试,1, 2, 3… 各位观众老爷们,晚上好!我是你们的老朋友,今天咱们来聊聊 WordPress 里一个神奇的函数:wp_get_current_user()

你肯定用过它,对不对? 只要你想知道当前是谁在登录,或者获取登录用户的相关信息,那它就是你的不二之选。 可是,你有没有好奇过,它是怎么知道“现在是谁”的? 它背后到底藏着什么秘密?

今天,咱们就来扒一扒它的源码,看看它到底是怎么“指认”当前用户的。 准备好了吗? Let’s dive in!

1. 故事的开端:请求与用户

在 Web 世界里,每次用户访问网站,都是一次请求。 WordPress 要识别用户,必须在这些请求中找到用户的“身份证明”。 这个身份证明通常就是 Cookie。

Cookie 就像一张小纸条,浏览器会帮我们保管着。 当用户登录 WordPress 时,服务器会在用户的浏览器里种下几个 Cookie,记录用户的身份信息。 以后用户每次访问网站,浏览器都会自动把这些 Cookie 捎带上,这样服务器就能认出用户了。

2. wp_get_current_user() 函数的真面目

好了,有了这个基础,咱们来看看 wp_get_current_user() 到底做了什么。 打开 wp-includes/pluggable.php 文件,找到这个函数,你会发现它其实很简单:

function wp_get_current_user() {
    global $current_user;

    if ( ! ( $current_user instanceof WP_User ) ) {
        $current_user = wp_get_current_user_info();
    }

    return $current_user;
}

是不是感觉有点意外? 这么常用的函数,代码竟然这么短! 它主要做了两件事:

  • 检查 $current_user 全局变量: $current_user 是一个全局变量,用来存储当前用户的 WP_User 对象。 函数首先检查这个变量是否已经存在,并且是否是 WP_User 的实例。
  • 如果 $current_user 不存在或不是 WP_User 实例: 调用 wp_get_current_user_info() 函数来获取用户信息,并赋值给 $current_user

所以,真正的秘密都在 wp_get_current_user_info() 函数里。 咱们接着往下看。

3. wp_get_current_user_info(): 抽丝剥茧

wp_get_current_user_info() 函数的代码稍微长一点,但也别怕,咱们一步一步来:

function wp_get_current_user_info() {
    static $cache = array();

    $user = null;

    if ( defined( 'XMLRPC_REQUEST' ) || defined( 'REST_REQUEST' ) || ( defined( 'WP_INSTALLING' ) && WP_INSTALLING ) || is_admin() ) {
        if ( isset( $_REQUEST['user_id'] ) && is_numeric( $_REQUEST['user_id'] ) ) {
            $user_id = (int) $_REQUEST['user_id'];
            $user    = get_userdata( $user_id );
        }
        // Grant access to any XMLRPC request
        // Grant access to REST requests that have authentication handled.
        // Grant access during install.
        // Grant access if a user is set in admin.
    }

    if ( empty( $user ) ) {
        /** This filter is documented in wp-includes/pluggable.php */
        $user_id = apply_filters( 'determine_current_user', false );
        if ( $user_id ) {
            $user = get_userdata( $user_id );
        }
    }

    if ( empty( $user ) ) {
        $user = wp_validate_auth_cookie();
    }

    if ( empty( $user ) ) {
        $user = new WP_User( 0 );
    }

    /**
     * Filters the current user's data.
     *
     * @since 2.1.0
     *
     * @param WP_User $user WP_User object for the current user.
     */
    $user = apply_filters( 'wp_get_current_user', $user );

    return $user;
}

看起来有点复杂,咱们把它拆解成几个部分:

  • 特殊情况处理: 函数首先检查一些特殊情况,比如是否是 XMLRPC 请求、REST 请求、安装过程或者在后台管理界面。 如果是这些情况,它可能会尝试从 $_REQUEST['user_id'] 获取用户 ID,然后用 get_userdata() 函数获取用户信息。 这些情况通常用于程序内部调用,或者是一些特殊的 API 请求。

  • determine_current_user 过滤器: 这是一个非常重要的过滤器。 开发者可以通过这个过滤器自定义用户识别逻辑。 例如,你可以根据自己的 Cookie 格式或者其他方式来确定用户 ID,然后返回。

  • wp_validate_auth_cookie() 这是最核心的部分! 如果前面的方法都失败了,函数会调用 wp_validate_auth_cookie() 函数来验证用户的认证 Cookie。 这个函数会检查 Cookie 的有效性,并从中提取用户 ID。

  • 默认用户: 如果所有方法都失败了,说明用户没有登录。 函数会创建一个 WP_User 对象,并将用户 ID 设置为 0,表示匿名用户。

  • wp_get_current_user 过滤器: 这是最后一个过滤器,允许开发者对最终的 WP_User 对象进行修改。

4. wp_validate_auth_cookie(): 验证身份的关键

咱们重点来看看 wp_validate_auth_cookie() 函数,因为它才是真正负责验证用户身份的关键。 这个函数的代码比较长,咱们只看关键部分:

function wp_validate_auth_cookie( $cookie = '', $scheme = '' ) {
    if ( ! $cookie ) {
        if ( isset( $_COOKIE[LOGGED_IN_COOKIE] ) ) {
            $cookie = $_COOKIE[LOGGED_IN_COOKIE];
        } else {
            return false;
        }
    }

    $cookie_elements = explode( '|', $cookie );
    if ( count( $cookie_elements ) !== 4 ) {
        return false;
    }

    list( $username, $expiration, $token, $hmac ) = $cookie_elements;

    $user = get_user_by( 'login', $username );
    if ( ! $user ) {
        return false;
    }

    $pass_frag = substr( $user->user_pass, 8, 4 );

    $key = wp_hash( $username . '|' . $pass_frag . '|' . $expiration . '|' . $token, 'logged_in' );
    $hash = hash_hmac( 'md5', $username . '|' . $expiration . '|' . $token, $key );

    if ( hash_equals( $hmac, $hash ) ) {
        return $user;
    }

    return false;
}

这个函数主要做了以下几件事:

  • 获取 Cookie: 首先,它尝试从 $_COOKIE[LOGGED_IN_COOKIE] 中获取用户的认证 Cookie。 LOGGED_IN_COOKIE 是一个常量,定义了 Cookie 的名称,通常是 wordpress_logged_in_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

  • 解析 Cookie: Cookie 的值是一个字符串,用 | 分隔成四个部分:用户名、过期时间、Token 和 HMAC。

  • 获取用户: 根据用户名,使用 get_user_by( 'login', $username ) 函数获取 WP_User 对象。

  • 验证 HMAC: HMAC (Hash-based Message Authentication Code) 是一种消息认证码,用于验证 Cookie 的完整性和真实性。 函数会根据用户名、密码片段、过期时间和 Token,重新计算 HMAC,然后与 Cookie 中的 HMAC 进行比较。 如果两者一致,说明 Cookie 是有效的,没有被篡改。

  • 返回用户: 如果 Cookie 验证通过,函数会返回 WP_User 对象。 否则,返回 false

5. 总结: wp_get_current_user() 的工作流程

现在,咱们可以总结一下 wp_get_current_user() 函数的工作流程了:

  1. 检查 $current_user 全局变量。
  2. 如果 $current_user 不存在或不是 WP_User 实例,则调用 wp_get_current_user_info()
  3. wp_get_current_user_info() 函数:
    • 处理特殊情况(XMLRPC、REST、安装、后台)。
    • 应用 determine_current_user 过滤器。
    • 调用 wp_validate_auth_cookie() 验证认证 Cookie。
    • 如果所有方法都失败,则创建匿名用户。
    • 应用 wp_get_current_user 过滤器。
  4. 返回 $current_user

用表格来总结一下:

函数 功能
wp_get_current_user() 获取当前登录用户的 WP_User 对象。 优先使用全局变量 $current_user,如果不存在则调用 wp_get_current_user_info() 获取。
wp_get_current_user_info() 确定当前用户的信息。 依次尝试以下方法:处理特殊情况,应用 determine_current_user 过滤器,调用 wp_validate_auth_cookie(),创建匿名用户。 最后应用 wp_get_current_user 过滤器。
wp_validate_auth_cookie() 验证用户的认证 Cookie。 从 Cookie 中提取用户名、过期时间、Token 和 HMAC,然后验证 HMAC 的有效性。 如果验证通过,则返回 WP_User 对象。

6. 深入思考:安全性与扩展性

通过分析源码,我们可以发现 wp_get_current_user() 函数的设计考虑了安全性与扩展性。

  • 安全性: wp_validate_auth_cookie() 函数使用 HMAC 来验证 Cookie 的完整性和真实性,防止 Cookie 被篡改。 同时,密码片段的使用也增加了破解 Cookie 的难度。

  • 扩展性: determine_current_userwp_get_current_user 过滤器允许开发者自定义用户识别逻辑和修改用户信息,提供了很大的灵活性。 例如,你可以使用第三方认证系统,或者根据用户的角色来动态修改用户权限。

7. 举个栗子:自定义用户识别逻辑

假设你想要根据一个名为 MY_CUSTOM_COOKIE 的 Cookie 来识别用户。 你可以使用 determine_current_user 过滤器来实现:

add_filter( 'determine_current_user', 'my_custom_determine_current_user' );

function my_custom_determine_current_user( $user_id ) {
    if ( ! empty( $_COOKIE['MY_CUSTOM_COOKIE'] ) ) {
        $custom_user_id = $_COOKIE['MY_CUSTOM_COOKIE'];
        if ( is_numeric( $custom_user_id ) ) {
            return (int) $custom_user_id;
        }
    }
    return $user_id;
}

这段代码会检查是否存在 MY_CUSTOM_COOKIE,如果存在,并且它的值是一个数字,则将其作为用户 ID 返回。 这样,wp_get_current_user() 函数就会根据你的自定义逻辑来识别用户了。

8. 总结与展望

今天,咱们一起深入了解了 wp_get_current_user() 函数的源码,揭开了它“指认”当前用户的秘密。 从 Cookie 的验证,到过滤器的应用,再到安全性与扩展性的考虑,这个函数的设计体现了 WordPress 的精髓。

希望今天的讲座能够帮助你更好地理解 WordPress 的用户认证机制,并在实际开发中更加灵活地使用 wp_get_current_user() 函数。

记住,代码的世界充满了乐趣,只要你敢于探索,就能发现更多的惊喜! 下次有机会,咱们再一起聊聊 WordPress 的其他有趣的函数。

感谢大家的聆听,咱们下期再见! (鞠躬)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注