探究 WordPress `is_user_logged_in()` 函数的源码:如何检查 `Cookie` 和全局变量判断登录状态。

大家好!欢迎来到今天的 WordPress 内幕小课堂。今天咱们来扒一扒 WordPress 中鼎鼎大名的 is_user_logged_in() 函数的底裤,看看它究竟是怎么判断用户是不是已经登录的。

开场白:登录状态的那些事儿

话说江湖上行走,身份很重要。在 WordPress 这个虚拟世界里,登录状态就代表着你的身份。你是游客,只能看看热闹;你是会员,可以发表评论;你是管理员,那就呼风唤雨,掌控一切。

那么,WordPress 怎么知道你是谁呢?靠的就是这个 is_user_logged_in() 函数。它就像一个门卫,检查你有没有“通行证”,也就是有没有登录。

第一步:全局变量的侦查

is_user_logged_in() 函数的第一反应,不是直接去翻你的 Cookie,而是先看看有没有什么全局变量已经告诉你答案了。

function is_user_logged_in() {
    global $current_user;

    if ( ! isset( $current_user ) ) {
        return false;
    }

    return ( $current_user instanceof WP_User && $current_user->ID > 0 );
}

这段代码看起来是不是很简单?咱们来一句一句地解读:

  1. global $current_user;:这句的意思是,把 $current_user 这个全局变量引入到当前函数的作用域里来。 $current_user 是 WordPress 用来保存当前用户信息的,如果用户登录了,它就会被赋值为一个 WP_User 对象。

  2. if ( ! isset( $current_user ) ) { return false; }:这句判断 $current_user 变量是否存在。如果不存在,说明 WordPress 还没初始化用户,那肯定没登录,直接返回 false

  3. return ( $current_user instanceof WP_User && $current_user->ID > 0 );:这句是关键。它做了两件事:

    • $current_user instanceof WP_User:判断 $current_user 是不是一个 WP_User 类的实例。只有登录用户才会被赋值为 WP_User 对象。
    • $current_user->ID > 0:判断 $current_user 对象的 ID 属性是否大于 0。 ID 属性代表用户的 ID,只有登录用户才有有效的 ID(大于 0)。

    如果这两个条件都满足,说明用户已经登录,返回 true。否则,返回 false

为什么要先检查全局变量?

你可能会问,为什么不直接去检查 Cookie 呢?原因很简单:效率!

如果每次都要解析 Cookie,那会消耗不少资源。而全局变量是已经加载到内存中的,访问速度快得多。所以,WordPress 先检查全局变量,如果全局变量已经有了用户信息,那就直接返回结果,省去了解析 Cookie 的麻烦。

第二步:Cookie 大作战

如果全局变量 $current_user 不存在,或者不是一个有效的 WP_User 对象,那就说明 WordPress 还没初始化用户,或者用户没有登录。这时候,is_user_logged_in() 函数就无能为力了,它只能返回 false

但是,真正负责设置 $current_user 的函数是 wp_get_current_user(),它才是真正和 Cookie 打交道的幕后英雄。让我们来深入了解一下 wp_get_current_user() 的工作原理。

function wp_get_current_user() {
    global $current_user, $wp_roles;

    if ( ! ( $current_user instanceof WP_User ) ) {
        wp_set_current_user();
    }

    return $current_user;
}

这段代码的核心在于 wp_set_current_user() 函数。如果 $current_user 不是一个 WP_User 对象,wp_get_current_user() 就会调用 wp_set_current_user() 来尝试设置 $current_user

接下来,咱们就要深入到 wp_set_current_user() 函数的内部,看看它是如何利用 Cookie 来判断用户是否登录的。

wp_set_current_user() 的秘密

wp_set_current_user() 函数的逻辑比较复杂,它会根据不同的情况采取不同的策略。

function wp_set_current_user( $id = 0, $name = '' ) {
    global $current_user, $wpdb;

    if ( ! ( $current_user instanceof WP_User ) ) {
        $current_user = new WP_User( $id, $name );
    }

    if ( ! $id ) {
        $id = apply_filters( 'determine_current_user', false );
    }

    if ( ! $id ) {
        if ( ! empty( $_COOKIE[ LOGGED_IN_COOKIE ] ) ) {
            $logged_in_cookie = $_COOKIE[ LOGGED_IN_COOKIE ];
            $cookie_elements = wp_parse_auth_cookie( $logged_in_cookie, 'logged_in' );

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

                if ( $expiration > time() ) {
                    $user = get_user_by( 'login', $username );

                    if ( $user && wp_validate_auth_cookie( $logged_in_cookie, 'logged_in', $user ) ) {
                        $id = $user->ID;
                    }
                }
            }
        } elseif ( ! empty( $_COOKIE[ SECURE_AUTH_COOKIE ] ) ) {
            // Secure Auth Cookie logic (for HTTPS) - similar to LOGGED_IN_COOKIE
            $secure_auth_cookie = $_COOKIE[ SECURE_AUTH_COOKIE ];
            $cookie_elements = wp_parse_auth_cookie( $secure_auth_cookie, 'secure_auth' );

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

                if ( $expiration > time() ) {
                    $user = get_user_by( 'login', $username );

                    if ( $user && wp_validate_auth_cookie( $secure_auth_cookie, 'secure_auth', $user ) ) {
                        $id = $user->ID;
                    }
                }
            }
        } elseif ( ! empty( $_COOKIE[ AUTH_COOKIE ] ) ) {
            // Auth Cookie logic - similar to LOGGED_IN_COOKIE
            $auth_cookie = $_COOKIE[ AUTH_COOKIE ];
            $cookie_elements = wp_parse_auth_cookie( $auth_cookie, '' ); //Empty string for default auth cookie

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

                if ( $expiration > time() ) {
                    $user = get_user_by( 'login', $username );

                    if ( $user && wp_validate_auth_cookie( $auth_cookie, '', $user ) ) {
                        $id = $user->ID;
                    }
                }
            }
        }
    }

    if ( $id ) {
        $current_user = new WP_User( $id );
    } else {
        $current_user = wp_get_anonymous_user();
    }

    do_action( 'set_current_user', $current_user, $id );
}

这段代码看着有点长,但其实逻辑还是比较清晰的。咱们一步一步来分析:

  1. 初始化 $current_user

    if ( ! ( $current_user instanceof WP_User ) ) {
        $current_user = new WP_User( $id, $name );
    }

    如果 $current_user 还不是一个 WP_User 对象,就创建一个新的 WP_User 对象。如果传入了 $id$name 参数,就用它们来初始化这个对象。

  2. determine_current_user 过滤器

    if ( ! $id ) {
        $id = apply_filters( 'determine_current_user', false );
    }

    这是一个非常重要的过滤器。它允许插件或主题通过自定义逻辑来设置当前用户 ID。如果插件或主题已经设置了用户 ID,那就直接使用它。

  3. Cookie 检查

    如果 $id 仍然是空的,那就开始检查 Cookie。WordPress 会检查三种 Cookie:

    • LOGGED_IN_COOKIE:用于普通登录的 Cookie。
    • SECURE_AUTH_COOKIE:用于 HTTPS 安全登录的 Cookie。
    • AUTH_COOKIE:用于管理后台登录的 Cookie。

    对于每种 Cookie,WordPress 都会做以下事情:

    • 检查 Cookie 是否存在:如果 Cookie 不存在,那就跳过。
    • 解析 Cookie:使用 wp_parse_auth_cookie() 函数来解析 Cookie,提取用户名、过期时间和 Token。
    • 验证 Cookie
      • 检查 Cookie 是否过期。
      • 根据用户名获取用户对象。
      • 使用 wp_validate_auth_cookie() 函数来验证 Cookie 的有效性。这个函数会检查 Cookie 中的 Token 是否和数据库中存储的 Token 匹配。

    如果 Cookie 验证成功,就获取用户的 ID,并将其赋值给 $id 变量。

  4. 设置 $current_user

    if ( $id ) {
        $current_user = new WP_User( $id );
    } else {
        $current_user = wp_get_anonymous_user();
    }

    如果 $id 变量有值,说明找到了登录用户,就创建一个新的 WP_User 对象,并将其赋值给 $current_user。如果 $id 变量仍然是空的,说明没有找到登录用户,就调用 wp_get_anonymous_user() 函数来获取一个匿名用户对象,并将其赋值给 $current_user

  5. set_current_user 动作

    do_action( 'set_current_user', $current_user, $id );

    这是一个动作钩子,允许插件或主题在设置当前用户之后执行一些自定义操作。

Cookie 的结构

咱们再来仔细看看 wp_parse_auth_cookie() 函数是如何解析 Cookie 的。WordPress 的登录 Cookie 并不是简单的用户名和密码,而是一个加密的字符串。这个字符串包含了以下信息:

字段 描述
username 用户名
expiration Cookie 的过期时间戳
token 用于验证 Cookie 有效性的 Token

wp_parse_auth_cookie() 函数会将这个加密的字符串解密,提取出这些信息,然后交给 wp_validate_auth_cookie() 函数进行验证。

wp_validate_auth_cookie() 的验证

wp_validate_auth_cookie() 函数会做以下验证:

  1. 检查 Cookie 是否过期:如果 Cookie 已经过期,那就验证失败。
  2. 检查 Token 是否匹配:WordPress 会在用户登录时生成一个 Token,并将其存储在数据库中。wp_validate_auth_cookie() 函数会从 Cookie 中提取 Token,然后和数据库中存储的 Token 进行比较。如果 Token 不匹配,那就验证失败。

总结

咱们来总结一下 is_user_logged_in() 函数的判断流程:

  1. 检查全局变量 $current_user 是否存在,并且是否是一个有效的 WP_User 对象。如果是,则返回 true
  2. 如果全局变量 $current_user 不存在或者不是一个有效的 WP_User 对象,则调用 wp_get_current_user() 函数。
  3. wp_get_current_user() 函数会调用 wp_set_current_user() 函数来设置 $current_user
  4. wp_set_current_user() 函数会依次检查 determine_current_user 过滤器和 Cookie,尝试获取用户 ID。
  5. 如果找到了用户 ID,就创建一个新的 WP_User 对象,并将其赋值给 $current_user
  6. 如果没找到用户 ID,就创建一个匿名用户对象,并将其赋值给 $current_user
  7. is_user_logged_in() 函数最终会根据 $current_user 对象的状态来判断用户是否登录。

一些有用的函数

除了 is_user_logged_in()wp_get_current_user()wp_set_current_user() 之外,还有一些其他的函数也和登录状态有关,比如:

  • wp_login():用户登录函数。
  • wp_logout():用户登出函数。
  • wp_get_current_user(): 获取当前用户信息。
  • get_current_user_id():获取当前用户的 ID。

最后的话

希望今天的课程能让你对 WordPress 的登录机制有更深入的了解。记住,理解这些底层的原理,才能更好地开发 WordPress 插件和主题。

下次再见!

发表回复

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