探究 WordPress `get_current_user_id()` 函数的源码:如何获取当前登录用户的 ID。

各位观众老爷们,大家好!今天咱就来聊聊 WordPress 里一个看似简单,但实际上背后有点小故事的函数:get_current_user_id()。 别看它名字平平无奇,它可是你在 WordPress 世界里辨认“你是谁”的关键钥匙。

开场白:身份的意义

想想看,你在一个网站上溜达,注册了账号,登录了进去。网站怎么知道你是你?它又怎么知道你有哪些权限,能看到哪些内容,能做什么操作? 这时候,用户的“身份”就显得尤为重要了。get_current_user_id() 就是负责告诉你,当前登录用户的 ID 是多少,相当于你的“身份证号码”。

第一部分:get_current_user_id() 函数的“前世今生”

这个函数的功能非常明确:获取当前已登录用户的 ID。如果用户未登录,它会返回 0。

<?php
/**
 * Get the ID of the current logged-in user.
 *
 * @since 2.1.0
 *
 * @return int 0 if no user is logged in.
 */
function get_current_user_id() {
    $current_user = wp_get_current_user();
    return ( $current_user instanceof WP_User ) ? $current_user->ID : 0;
}

怎么样,是不是很简单? 别急,这只是冰山一角。让我们一层层扒开它的“内衣”。

第二部分:拨开云雾见真身:wp_get_current_user() 函数

从上面的代码可以看到,get_current_user_id() 本身并没有直接去数据库里查用户信息,而是调用了另一个函数:wp_get_current_user()。 这个函数才是真正的大佬,负责获取当前用户的 WP_User 对象。

让我们深入 wp-includes/pluggable.php 文件,看看 wp_get_current_user() 函数的真面目:

<?php
/**
 * Retrieve the current user object.
 *
 * @since 2.0.0
 *
 * @return WP_User WP_User object of the current user.
 */
function wp_get_current_user() {
    global $current_user, $wp_roles;

    if ( ! ( $current_user instanceof WP_User ) ) {
        if ( ! empty( $_COOKIE[ LOGGED_IN_COOKIE ] ) ) {
            wp_set_current_user();
        } else {
            $current_user = new WP_User( 0 );
        }
    }

    return $current_user;
}

这个函数做了几件事:

  1. 全局变量检查: 首先,它检查全局变量 $current_user 是否已经是一个 WP_User 对象。 如果是,直接返回,避免重复查询。 这是性能优化的一个体现,避免不必要的数据库查询。
  2. Cookie 检查: 如果 $current_user 不是 WP_User 对象,它会检查是否存在名为 LOGGED_IN_COOKIE 的 Cookie。 这个 Cookie 存储着用户的认证信息。
  3. 设置当前用户: 如果存在 LOGGED_IN_COOKIE,函数会调用 wp_set_current_user() 来根据 Cookie 中的信息设置 $current_user 对象。
  4. 创建匿名用户: 如果没有 LOGGED_IN_COOKIE,则创建一个 ID 为 0 的 WP_User 对象,代表匿名用户。

重点来了:wp_set_current_user() 函数的“乾坤大挪移”

wp_set_current_user() 函数才是真正从 Cookie 中提取用户信息,并填充 $current_user 对象的核心函数。 继续深入 wp-includes/pluggable.php 文件:

<?php
/**
 * Sets the current user.
 *
 * @since 2.5.0
 *
 * @param int|WP_User $id User ID or WP_User object.
 */
function wp_set_current_user( $id = 0 ) {
    global $current_user;

    if ( ! ( $id instanceof WP_User ) ) {
        $id = (int) $id;
        if ( empty( $id ) && ! empty( $_COOKIE[ LOGGED_IN_COOKIE ] ) ) {
            $cookie = $_COOKIE[ LOGGED_IN_COOKIE ];
            $cookie_elements = wp_parse_auth_cookie( $cookie, 'logged_in' );

            if ( ! empty( $cookie_elements ) ) {
                $scheme = $cookie_elements['scheme'];
                $username = $cookie_elements['username'];
                $expiration = $cookie_elements['expiration'];
                $password = $cookie_elements['password'];

                /** This filter is documented in wp-includes/pluggable.php */
                $username = apply_filters( 'auth_cookie_username', $username, $scheme );

                /**
                 * Fires before the user is switched.
                 *
                 * @since 5.2.0
                 *
                 * @param int $old_user_id The ID of the old current user.
                 * @param int $new_user_id The ID of the new current user.
                 */
                do_action( 'wp_before_switch_user', get_current_user_id(), 0 );

                $user = wp_authenticate_cookie( $cookie, $scheme );

                /**
                 * Fires after the user is switched.
                 *
                 * @since 5.2.0
                 *
                 * @param int $old_user_id The ID of the old current user.
                 * @param int $new_user_id The ID of the new current user.
                 */
                do_action( 'wp_after_switch_user', get_current_user_id(), $user ? $user->ID : 0 );

                if ( is_wp_error( $user ) ) {
                    $current_user = new WP_User( 0 );
                    return;
                }

                if ( $user ) {
                    $id = $user->ID;
                }
            }
        }
        if ( ! empty( $id ) ) {
            $user = get_userdata( $id );
        } else {
            $user = new WP_User( 0 );
        }
    } else {
        $user = $id;
    }

    $current_user = $user;

    wp_cache_set( $id, $current_user, 'users' );

    /**
     * Fires after the current user is set.
     *
     * @since 2.1.0
     *
     * @param WP_User $user WP_User object of the current user.
     */
    do_action( 'set_current_user', $current_user );
}

这个函数稍微复杂一点,但我们可以分解来看:

  1. 参数处理: 接受一个用户 ID 或者 WP_User 对象作为参数。 如果传入的是 WP_User 对象,直接使用;如果是 ID,则尝试根据 ID 获取用户信息。 如果没有传入任何参数,则从Cookie中尝试解析用户信息。
  2. Cookie 解析: 如果没有传入 ID,并且存在 LOGGED_IN_COOKIE,函数会调用 wp_parse_auth_cookie() 函数来解析 Cookie 中的信息,包括用户名、过期时间、密码哈希等。
  3. 用户认证: 接下来,函数会使用 wp_authenticate_cookie() 函数来验证 Cookie 的有效性。 这个函数会检查 Cookie 中的信息是否与数据库中的用户信息匹配。如果验证失败,则认为用户未登录。
  4. 获取用户数据: 如果 Cookie 验证成功,或者传入了有效的用户 ID,函数会调用 get_userdata() 函数来从数据库中获取用户的详细信息,并创建一个 WP_User 对象。
  5. 设置全局变量: 最后,函数会将获取到的 WP_User 对象赋值给全局变量 $current_user,并将其缓存起来,以便下次使用。

wp_parse_auth_cookie():Cookie 的秘密花园

wp_parse_auth_cookie() 函数负责解析 WordPress 用于登录认证的 Cookie。 让我们看看它的庐山真面目(wp-includes/pluggable.php):

<?php
/**
 * Parses a authentication cookie.
 *
 * @since 2.5.0
 *
 * @param string $cookie Authentication cookie.
 * @param string $scheme Optional. Authentication scheme. Default is 'auth'.
 * @return array|false An array of the authentication cookie fields, or false if the cookie is invalid.
 */
function wp_parse_auth_cookie( $cookie = '', $scheme = '' ) {
    if ( empty( $cookie ) ) {
        return false;
    }

    $cookie_elements = explode( '|', $cookie );

    if ( count( $cookie_elements ) !== 4 ) {
        return false;
    }

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

    if ( empty( $username ) || ! is_numeric( $expiration ) || empty( $token ) || empty( $hmac ) ) {
        return false;
    }

    $username = sanitize_user( $username, 'username' );
    $expiration = intval( $expiration );

    /**
     * Filters the username from the authentication cookie.
     *
     * @since 2.8.0
     *
     * @param string $username The username from the authentication cookie.
     * @param string $scheme   Authentication scheme.
     */
    $username = apply_filters( 'auth_cookie_username', $username, $scheme );

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

    if ( ! hash_equals( $hmac, $key ) ) {
        return false;
    }

    return array(
        'username'   => $username,
        'expiration' => $expiration,
        'token'      => $token,
        'hmac'       => $hmac,
        'scheme'     => $scheme,
    );
}

这个函数的主要工作是:

  1. 分割 Cookie: 将 Cookie 字符串按照 | 符号分割成四个部分:用户名、过期时间、令牌(token)和 HMAC(哈希消息认证码)。
  2. 数据校验: 检查分割后的数据是否完整且有效。
  3. 安全校验: 使用 HMAC 算法验证 Cookie 的完整性,防止 Cookie 被篡改。
  4. 返回数据: 如果验证通过,则返回一个包含用户名、过期时间、令牌和 HMAC 的数组。

wp_authenticate_cookie():Cookie 的终极审判者

wp_authenticate_cookie() 函数负责验证 Cookie 的有效性,判断用户是否真的已经登录。 让我们看看它的代码(wp-includes/pluggable.php):

<?php
/**
 * Authenticates a user based on a authentication cookie.
 *
 * @since 2.5.0
 *
 * @param string $cookie Authentication cookie.
 * @param string $scheme Authentication scheme. Default is 'logged_in'.
 * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
 */
function wp_authenticate_cookie( $cookie = '', $scheme = '' ) {
    if ( empty( $cookie ) ) {
        return new WP_Error( 'auth_cookie_empty', __( 'Authentication cookie is empty.' ) );
    }

    $cookie_elements = wp_parse_auth_cookie( $cookie, $scheme );

    if ( ! $cookie_elements ) {
        return new WP_Error( 'auth_cookie_invalid', __( 'Authentication cookie is invalid.' ) );
    }

    $username   = $cookie_elements['username'];
    $expiration = $cookie_elements['expiration'];
    $token      = $cookie_elements['token'];

    if ( $expiration < time() ) {
        return new WP_Error( 'auth_cookie_expired', __( 'Authentication cookie is expired.' ) );
    }

    $user = get_user_by( 'login', $username );

    if ( ! $user ) {
        return new WP_Error( 'auth_cookie_bad_username', __( 'Username does not exist.' ) );
    }

    if ( ! wp_validate_auth_cookie( $cookie, $user->ID, $scheme, $token ) ) {
        return new WP_Error( 'auth_cookie_bad_hash', __( 'Authentication cookie is incorrect.' ) );
    }

    /**
     * Fires after the authentication cookie is validated.
     *
     * @since 3.5.0
     *
     * @param WP_User $user WP_User object.
     * @param string  $cookie The authentication cookie.
     * @param string  $scheme Authentication scheme.
     */
    do_action( 'auth_cookie_valid', $user, $cookie, $scheme );

    return $user;
}

这个函数的主要工作是:

  1. 解析 Cookie: 调用 wp_parse_auth_cookie() 函数解析 Cookie。
  2. 过期时间验证: 检查 Cookie 是否过期。
  3. 用户名验证: 根据用户名查找用户是否存在。
  4. 哈希验证: 调用 wp_validate_auth_cookie() 函数验证 Cookie 的哈希值是否正确,防止 Cookie 被篡改。
  5. 返回用户对象: 如果所有验证都通过,则返回 WP_User 对象;否则,返回一个 WP_Error 对象。

wp_validate_auth_cookie():哈希校验的“守门员”

wp_validate_auth_cookie() 函数是验证 Cookie 安全性的最后一道防线。 它负责验证 Cookie 中的哈希值是否与服务器端计算出的哈希值匹配,以确保 Cookie 没有被篡改。

<?php
/**
 * Validates authentication cookie.
 *
 * @since 2.5.0
 *
 * @param string $cookie Authentication cookie.
 * @param int    $user_id User ID.
 * @param string $scheme Authentication scheme. Default is 'logged_in'.
 * @param string $token User's session token to prevent replay attacks.
 * @return bool True if the cookie is valid, false otherwise.
 */
function wp_validate_auth_cookie( $cookie = '', $user_id = 0, $scheme = '', $token = '' ) {
    if ( empty( $cookie ) ) {
        return false;
    }

    $user = get_userdata( $user_id );

    if ( ! $user ) {
        return false;
    }

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

    $key = wp_hash( $user->user_login . '|' . $pass_frag . '|' . $token, $scheme );
    $key = hash_hmac( 'md5', $user->user_login . '|' . $pass_frag . '|' . $token, $key );

    $cookie_elements = wp_parse_auth_cookie( $cookie, $scheme );

    if ( ! $cookie_elements ) {
        return false;
    }

    $hmac = $cookie_elements['hmac'];

    if ( ! hash_equals( $hmac, $key ) ) {
        return false;
    }

    return true;
}

这个函数的主要工作是:

  1. 获取用户信息: 根据用户 ID 获取用户信息。
  2. 生成哈希值: 使用用户的登录名、密码片段和令牌生成一个哈希值。
  3. 比较哈希值: 将生成的哈希值与 Cookie 中的哈希值进行比较。如果匹配,则认为 Cookie 有效;否则,认为 Cookie 被篡改。

总结:get_current_user_id() 的“寻根之旅”

现在,让我们把整个流程串起来:

  1. 你调用 get_current_user_id() 函数。
  2. get_current_user_id() 函数调用 wp_get_current_user() 函数。
  3. wp_get_current_user() 函数检查全局变量 $current_user 是否已经设置。
  4. 如果 $current_user 未设置,wp_get_current_user() 函数检查是否存在 LOGGED_IN_COOKIE
  5. 如果存在 LOGGED_IN_COOKIEwp_get_current_user() 函数调用 wp_set_current_user() 函数。
  6. wp_set_current_user() 函数解析 LOGGED_IN_COOKIE,并使用 wp_authenticate_cookie() 函数验证 Cookie 的有效性。
  7. wp_authenticate_cookie() 函数使用 wp_validate_auth_cookie() 函数验证 Cookie 的哈希值。
  8. 如果 Cookie 验证成功,wp_set_current_user() 函数从数据库中获取用户的详细信息,并设置全局变量 $current_user
  9. wp_get_current_user() 函数返回 $current_user 对象。
  10. get_current_user_id() 函数从 $current_user 对象中获取用户 ID 并返回。

用表格总结一下:

函数名 功能 依赖函数
get_current_user_id() 获取当前登录用户的 ID。 wp_get_current_user()
wp_get_current_user() 获取当前登录用户的 WP_User 对象。 wp_set_current_user()
wp_set_current_user() 设置当前用户。 wp_parse_auth_cookie(), wp_authenticate_cookie(), get_userdata()
wp_parse_auth_cookie() 解析 WordPress 用于登录认证的 Cookie。
wp_authenticate_cookie() 验证 Cookie 的有效性,判断用户是否真的已经登录。 wp_parse_auth_cookie(), wp_validate_auth_cookie(), get_user_by()
wp_validate_auth_cookie() 验证 Cookie 的哈希值是否正确,防止 Cookie 被篡改。 get_userdata(), wp_parse_auth_cookie()
get_userdata() 根据用户 ID 从数据库中获取用户的详细信息。
get_user_by() 根据用户名从数据库中获取用户的详细信息。

第三部分:实战演练:get_current_user_id() 的应用场景

了解了 get_current_user_id() 的工作原理,我们来看看它在实际开发中有哪些应用场景:

  • 权限控制: 根据用户 ID 判断用户是否具有执行特定操作的权限。
<?php
$current_user_id = get_current_user_id();
if ( current_user_can( 'edit_posts' ) ) {
  // 用户具有编辑文章的权限
  echo '<a href="' . admin_url( 'post-new.php' ) . '">撰写新文章</a>';
} else {
  echo '您没有权限撰写新文章。';
}
?>
  • 个性化内容展示: 根据用户 ID 显示个性化的内容。
<?php
$current_user_id = get_current_user_id();
$user_info = get_userdata( $current_user_id );
if ( $user_info ) {
  echo '欢迎您,' . $user_info->display_name . '!';
} else {
  echo '欢迎您,游客!';
}
?>
  • 用户行为追踪: 记录用户的操作行为,例如用户发表的文章、评论等。
<?php
$current_user_id = get_current_user_id();
// 将用户 ID 存储到文章的自定义字段中
update_post_meta( $post_id, 'author_id', $current_user_id );
?>
  • 自定义用户界面: 根据用户 ID 调整用户界面的显示方式。
<?php
$current_user_id = get_current_user_id();
if ( $current_user_id == 1 ) {
  // 管理员用户
  echo '<style>.admin-style { color: red; }</style>';
} else {
  // 普通用户
  echo '<style>.user-style { color: blue; }</style>';
}
?>

第四部分:安全注意事项

虽然 get_current_user_id() 函数本身没有安全漏洞,但在使用时需要注意以下几点:

  • 不要直接信任用户 ID: 用户 ID 可能会被篡改,因此不要直接将其用于安全敏感的操作,例如数据库查询。 应该始终使用 WordPress 提供的权限控制函数(例如 current_user_can())来验证用户的权限。
  • 防止跨站脚本攻击(XSS): 对从数据库中获取的用户信息进行转义,防止 XSS 攻击。
  • 防止跨站请求伪造(CSRF): 在执行敏感操作时,使用 nonce 来防止 CSRF 攻击。

第五部分:高级技巧:缓存和性能优化

WordPress 已经对用户信息进行了缓存,因此通常情况下,get_current_user_id() 函数的性能不会成为瓶颈。 然而,在高流量的网站上,仍然可以考虑以下优化措施:

  • 对象缓存: 使用对象缓存插件(例如 Memcached 或 Redis)来缓存 WP_User 对象,减少数据库查询。
  • 避免重复调用: 避免在同一页面中多次调用 get_current_user_id() 函数。 可以将用户 ID 存储到变量中,并在需要时重复使用。

总结陈词:身份认证的基石

get_current_user_id() 函数是 WordPress 身份认证体系中的一个基础组件。 理解它的工作原理,可以帮助我们更好地理解 WordPress 的用户管理机制,并编写更安全、更高效的代码。 记住,在 WordPress 的世界里,知道你是谁,才能更好地做你想做的事!

好了,今天的讲座就到这里。希望大家有所收获!下次有机会再见!

发表回复

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