深入解读 WordPress `wp_set_auth_cookie()` 函数源码:用户认证 Cookie 的设置与过期时间。

咳咳,各位观众老爷们,晚上好!我是你们今晚的导游,啊不,是讲师,带大家一起深入 WordPress 的神秘后花园,看看 wp_set_auth_cookie() 这个小家伙,是怎么帮我们搞定用户认证这件大事的。准备好你们的放大镜和咖啡,我们这就开始!

第一章:Cookie,身份的通行证

在深入代码之前,我们先来聊聊 Cookie 这个东西。想象一下,你进了一个会员制俱乐部,门口的保安蜀黍给你发了一个小卡片,上面写着你的会员信息。下次你再来,只要把这个卡片给保安看,他就能认出你,然后放你进去。这个小卡片,就是 Cookie 的作用。

在 Web 世界里,Cookie 是一小段文本信息,存储在用户的浏览器里。当用户再次访问网站时,浏览器会自动把这些 Cookie 发送给服务器,服务器通过 Cookie 里的信息来识别用户,然后提供相应的服务。

第二章:wp_set_auth_cookie(),Cookie 的制造者

在 WordPress 里,wp_set_auth_cookie() 函数就是那个制造会员卡片的工厂。它的主要作用是设置用户认证的 Cookie,让 WordPress 知道“哦,原来是你来了!”。

这个函数藏身于 wp-includes/pluggable.php 文件里,我们先来看看它的庐山真面目:

function wp_set_auth_cookie( $user_id, $remember = false, $secure = '' ) {
    if ( ! $user_id ) {
        return;
    }

    $secure_cookie = is_ssl();
    /**
     * Filters whether the auth cookie should be secure.
     *
     * @since 3.1.0
     *
     * @param bool   $secure_cookie Whether the auth cookie should be secure.
     * @param int    $user_id       User ID.
     * @param string $scheme        Authentication scheme. Default 'auth'.
     */
    $secure_cookie = apply_filters( 'secure_auth_cookie', $secure_cookie, $user_id, 'auth' );

    if ( ! empty( $secure ) ) {
        $secure_cookie = $secure;
    }

    $expiration = time() + ( $remember ? DAY_IN_SECONDS * 14 : HOUR_IN_SECONDS * 12 );

    if ( $remember ) {
        /**
         * Filters the duration of the authentication cookie expiration period.
         *
         * @since 2.8.0
         *
         * @param int    $length   Duration of the expiration period in seconds.
         * @param int    $user_id  User ID.
         * @param bool   $remember Whether the cookie is for remembering the user.
         * @param string $scheme   Authentication scheme. Default 'auth'.
         */
        $expiration = apply_filters( 'auth_cookie_expiration', $expiration, $user_id, $remember, 'auth' );
    }

    $username = wp_get_current_user()->user_login;
    $key = wp_hash( $username . '|' . $expiration . '|' . wp_generate_password( 43, false ), 'auth' );

    /**
     * Fires immediately before the authentication cookie is set.
     *
     * @since 4.3.0
     *
     * @param string $cookie   The authentication cookie name.
     * @param int    $user_id  User ID.
     * @param bool   $remember Whether the cookie is for remembering the user.
     * @param string $username User's username.
     * @param string $expiration The time the cookie expires.
     * @param string $scheme Authentication scheme. Default 'auth'.
     */
    do_action( 'set_auth_cookie', $cookie, $user_id, $remember, $username, $expiration, 'auth' );

    setcookie( AUTH_COOKIE, wp_generate_auth_cookie( $user_id, $expiration, 'auth', $key ), $expiration, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN, $secure_cookie, true );
    setcookie( SECURE_AUTH_COOKIE, wp_generate_auth_cookie( $user_id, $expiration, 'secure_auth', $key ), $expiration, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN, true, true );

    if ( $remember ) {
        wp_removable_sessions();
    }

    /**
     * Fires immediately after the authentication cookie is set.
     *
     * @since 4.3.0
     *
     * @param string $cookie   The authentication cookie name.
     * @param int    $user_id  User ID.
     * @param bool   $remember Whether the cookie is for remembering the user.
     * @param string $username User's username.
     * @param string $expiration The time the cookie expires.
     * @param string $scheme Authentication scheme. Default 'auth'.
     */
    do_action( 'set_auth_cookie', $cookie, $user_id, $remember, $username, $expiration, 'auth' );
}

让我们来逐行解读这段代码,看看它都做了些什么:

  1. 参数检查:

    if ( ! $user_id ) {
        return;
    }

    首先,它检查 $user_id 是否为空。如果为空,说明没有用户 ID,那就直接返回,不做任何操作。毕竟,没有用户,给谁发会员卡呢?

  2. 安全 Cookie 检查:

    $secure_cookie = is_ssl();
    
    $secure_cookie = apply_filters( 'secure_auth_cookie', $secure_cookie, $user_id, 'auth' );
    
    if ( ! empty( $secure ) ) {
        $secure_cookie = $secure;
    }

    这段代码用来确定是否设置 secure 属性。secure 属性意味着 Cookie 只能通过 HTTPS 连接传输,保证了 Cookie 的安全性。

    • is_ssl() 函数检查当前是否使用了 HTTPS 连接。
    • apply_filters( 'secure_auth_cookie', ...) 允许开发者通过过滤器修改 secure_cookie 的值。这为开发者提供了更大的灵活性。
    • 如果函数调用时显式传入了 $secure 参数,那么就使用传入的值。
  3. 过期时间计算:

    $expiration = time() + ( $remember ? DAY_IN_SECONDS * 14 : HOUR_IN_SECONDS * 12 );
    
    if ( $remember ) {
        $expiration = apply_filters( 'auth_cookie_expiration', $expiration, $user_id, $remember, 'auth' );
    }

    这里是计算 Cookie 的过期时间。

    • 如果 $remembertrue,表示用户选择了“记住我”,那么 Cookie 的过期时间设置为 14 天 ( DAY_IN_SECONDS * 14 )。
    • 如果 $rememberfalse,表示用户没有选择“记住我”,那么 Cookie 的过期时间设置为 12 小时 ( HOUR_IN_SECONDS * 12 )。
    • apply_filters( 'auth_cookie_expiration', ...) 同样允许开发者通过过滤器修改 Cookie 的过期时间。
  4. 生成 Cookie 的 Key:

    $username = wp_get_current_user()->user_login;
    $key = wp_hash( $username . '|' . $expiration . '|' . wp_generate_password( 43, false ), 'auth' );

    这部分代码生成一个用于 Cookie 加密的 Key。

    • 它首先获取当前用户的用户名 (wp_get_current_user()->user_login)。
    • 然后,将用户名、过期时间和一个随机字符串 ( wp_generate_password( 43, false ) ) 连接起来,并使用 wp_hash() 函数进行哈希,生成最终的 Key。
    • wp_hash() 函数使用 WordPress 的盐 (salt) 来增加哈希的安全性。
  5. 触发动作 (Action):

    do_action( 'set_auth_cookie', $cookie, $user_id, $remember, $username, $expiration, 'auth' );

    在设置 Cookie 之前和之后,都会触发一个 set_auth_cookie 动作。这允许其他插件或主题在 Cookie 设置前后执行一些自定义的操作。

  6. 设置 Cookie:

    setcookie( AUTH_COOKIE, wp_generate_auth_cookie( $user_id, $expiration, 'auth', $key ), $expiration, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN, $secure_cookie, true );
    setcookie( SECURE_AUTH_COOKIE, wp_generate_auth_cookie( $user_id, $expiration, 'secure_auth', $key ), $expiration, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN, true, true );

    这才是真正设置 Cookie 的地方。setcookie() 是 PHP 的内置函数,用于设置 Cookie。这里设置了两个 Cookie:

    • AUTH_COOKIE:用于普通的身份验证。
    • SECURE_AUTH_COOKIE:用于安全的身份验证 (HTTPS)。

    wp_generate_auth_cookie() 函数用于生成 Cookie 的值,它会根据用户 ID、过期时间、认证方案和 Key 生成一个加密的字符串。

    其他的参数,比如 PLUGINS_COOKIE_PATHCOOKIE_DOMAIN,定义了 Cookie 的路径和域名。$secure_cookie 决定了 Cookie 是否只能通过 HTTPS 连接传输。最后一个参数 true 表示 Cookie 只能通过 HTTP 协议访问,不能被 JavaScript 访问,这可以防止 XSS 攻击。

  7. 移除会话 (如果选择了 "记住我"):

    if ( $remember ) {
        wp_removable_sessions();
    }

    如果用户选择了“记住我”,那么会调用 wp_removable_sessions() 函数来管理可移除的会话。这通常用于增强安全性,允许用户在必要时注销所有设备上的会话。

第三章:参数详解,Cookie 的说明书

wp_set_auth_cookie() 函数接收三个参数:

参数 类型 描述
$user_id int 用户的 ID。这是必须的参数,告诉 WordPress 你要给哪个用户设置 Cookie。
$remember bool 是否记住用户。如果设置为 true,Cookie 的过期时间会更长,用户下次访问网站时会自动登录。如果设置为 false,Cookie 的过期时间会相对较短,用户关闭浏览器后需要重新登录。
$secure string 是否强制使用安全 Cookie。这个参数比较特殊,它可以覆盖函数内部对 HTTPS 连接的判断。如果你的网站同时支持 HTTP 和 HTTPS,并且你希望在 HTTP 连接下也使用安全 Cookie,可以把这个参数设置为 true。但是,这样做可能会导致一些问题,所以要谨慎使用。通常情况下,让函数自动判断是否使用 HTTPS 连接是最好的选择。如果你使用反向代理或者负载均衡,并且SSL卸载发生在代理层,那么WordPress可能无法正确检测到HTTPS。 这时,你可以设置$_SERVER['HTTPS'] = 'on';在wp-config.php中,或者使用$secure参数来强制开启安全cookie。

第四章:过期时间的秘密,Cookie 的保质期

Cookie 的过期时间非常重要,它决定了用户在多长时间内可以保持登录状态。wp_set_auth_cookie() 函数根据 $remember 参数来设置 Cookie 的过期时间。

  • “记住我” ( $remember = true ): Cookie 的过期时间设置为 14 天。这意味着用户在 14 天内可以不用重新登录,就可以访问网站。
  • 不“记住我” ( $remember = false ): Cookie 的过期时间设置为 12 小时。这意味着用户关闭浏览器后,Cookie 就失效了,下次访问网站需要重新登录。

当然,WordPress 也提供了过滤器 auth_cookie_expiration,允许开发者自定义 Cookie 的过期时间。

例如,如果你想把“记住我”的 Cookie 过期时间设置为 30 天,可以这样写:

add_filter( 'auth_cookie_expiration', 'my_custom_auth_cookie_expiration', 10, 3 );

function my_custom_auth_cookie_expiration( $expiration, $user_id, $remember ) {
    if ( $remember ) {
        return DAY_IN_SECONDS * 30;
    }

    return $expiration;
}

这段代码通过 add_filter() 函数注册了一个过滤器,当 auth_cookie_expiration 被调用时,my_custom_auth_cookie_expiration() 函数会被执行。这个函数会判断 $remember 是否为 true,如果是,就把 Cookie 的过期时间设置为 30 天。

第五章:wp_generate_auth_cookie(),Cookie 内容的制造者

我们之前提到过 wp_generate_auth_cookie() 函数,它负责生成 Cookie 的值。这个函数也藏在 wp-includes/pluggable.php 文件里,我们来看看它的源码:

function wp_generate_auth_cookie( $user_id, $expiration, $scheme = 'auth', $token = '' ) {
    $user = get_userdata( $user_id );
    $pass_frag = substr( wp_hash( $user->user_pass, 'password' ), 8, 4 );

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

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

    $cookie = $user->user_login . '|' . $expiration . '|' . $hash;

    /**
     * Filters the authentication cookie.
     *
     * @since 2.5.0
     *
     * @param string $cookie    Authentication cookie.
     * @param int    $user_id   User ID.
     * @param int    $expiration Time the cookie expires in seconds.
     * @param string $scheme    Authentication scheme. Default 'auth'.
     */
    return apply_filters( 'auth_cookie', $cookie, $user_id, $expiration, $scheme );
}

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

  1. 获取用户信息:

    $user = get_userdata( $user_id );

    通过用户 ID 获取用户的详细信息,包括用户名、密码等。

  2. 获取密码片段:

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

    获取用户密码的哈希值,并截取其中的一部分作为密码片段。这个片段用于增加 Cookie 的安全性。

  3. 生成 Key:

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

    使用用户 ID、用户名、密码片段、过期时间、认证方案和 Token (如果存在) 生成一个 Key。这个 Key 用于对 Cookie 进行加密。

  4. 生成 Hash:

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

    使用 HMAC-MD5 算法,对用户名、过期时间、认证方案和 Token 进行哈希,生成一个 Hash 值。HMAC 算法使用 Key 对数据进行哈希,可以防止篡改。

  5. 生成 Cookie:

    $cookie = $user->user_login . '|' . $expiration . '|' . $hash;

    将用户名、过期时间和 Hash 值连接起来,生成最终的 Cookie 值。

  6. 过滤 Cookie:

    return apply_filters( 'auth_cookie', $cookie, $user_id, $expiration, $scheme );

    使用 auth_cookie 过滤器,允许开发者修改 Cookie 的值。

第六章:安全考量,Cookie 的保护伞

安全性是用户认证的关键。wp_set_auth_cookie() 函数在设计时考虑了以下安全因素:

  • HTTPS: 通过 secure 属性,确保 Cookie 只能通过 HTTPS 连接传输,防止 Cookie 被窃听。
  • HTTPOnly: 通过设置 HTTPOnly 属性,防止 JavaScript 访问 Cookie,降低 XSS 攻击的风险。
  • 哈希: 使用哈希算法对 Cookie 的值进行加密,防止 Cookie 被篡改。
  • 过期时间: 合理设置 Cookie 的过期时间,防止 Cookie 长时间有效,增加安全风险。
  • 盐 (Salt): WordPress 使用盐来增加哈希的安全性,防止彩虹表攻击。

第七章:总结,Cookie 的旅程

wp_set_auth_cookie() 函数是 WordPress 用户认证的核心。它负责生成和设置用户认证的 Cookie,让 WordPress 能够识别用户并提供相应的服务。通过理解这个函数的源码和参数,我们可以更好地控制用户认证的行为,并根据自己的需求进行定制。

我们来简单回顾一下今天的内容:

  • Cookie 是用于身份验证的小卡片。
  • wp_set_auth_cookie() 函数负责生成和设置 Cookie。
  • $remember 参数决定了 Cookie 的过期时间。
  • wp_generate_auth_cookie() 函数负责生成 Cookie 的值。
  • WordPress 在设计 Cookie 时考虑了多种安全因素。

好了,今天的讲座就到这里。希望大家有所收获!如果还有什么疑问,欢迎随时提问。下次再见!

发表回复

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