嘿,大家好!今天咱们来扒一扒 WordPress 里的一个关键函数——determine_current_user()
。这哥们儿负责在茫茫人海(网络流量)中,识别出当前访问者是谁,也就是“你是谁,从哪里来,要到哪里去”的网络版。
咱们的目标是:深入源码,搞清楚它怎么靠 Cookie 和会话信息,判断出当前用户是谁。准备好了吗?Let’s dive in!
一、开场白:角色与职责
在WordPress世界里,用户身份验证是个核心环节。determine_current_user()
函数就像个门卫,每次有人来访,它都要检查访客的“身份证”(Cookie 或会话信息),然后决定是否放行,以及放行后赋予什么权限。
二、源码初探:在哪里,长什么样?
determine_current_user()
函数藏身于 wp-includes/pluggable.php
文件中。打开它,你可能会被密密麻麻的代码吓一跳。别怕,咱们一步步来。
function determine_current_user() {
/** WordPress Hooks */
do_action( 'determine_current_user' );
global $current_user;
if ( ! empty( $current_user->ID ) ) {
return;
}
// Get the current user ID from a cookie, if present.
if ( ! empty( $_COOKIE[ LOGGED_IN_COOKIE ] ) ) {
wp_set_current_user( wp_validate_auth_cookie() );
} elseif ( ! empty( $_COOKIE[ SECURE_AUTH_COOKIE ] ) && is_ssl() ) {
wp_set_current_user( wp_validate_auth_cookie( '', 'secure' ) );
} elseif ( ! empty( $_COOKIE[ AUTH_COOKIE ] ) ) {
wp_set_current_user( wp_validate_auth_cookie( '', 'auth' ) );
}
// If the user ID is still empty, try to get it from the session.
if ( empty( $current_user->ID ) && is_user_logged_in() ) {
$current_user = wp_get_current_user(); //This retrieves the user from the session
}
/** WordPress Hooks */
do_action( 'after_determine_current_user' );
}
这段代码看起来有点长,但逻辑其实挺清晰的:
- 检查是否已经确定了用户: 如果
$current_user->ID
不为空,说明之前已经判断过了,直接返回。 - 从 Cookie 中获取用户 ID: 依次检查
LOGGED_IN_COOKIE
、SECURE_AUTH_COOKIE
和AUTH_COOKIE
这三个 Cookie。如果存在,就调用wp_validate_auth_cookie()
函数来验证 Cookie,并设置当前用户。 - 从会话中获取用户 ID: 如果 Cookie 没用,就检查会话。如果用户已经登录,就从会话中获取用户信息。
- 钩子: 在函数执行前后,分别调用
do_action()
函数,允许其他插件或主题扩展功能。
三、Cookie 侦探:wp_validate_auth_cookie()
详解
wp_validate_auth_cookie()
函数是 Cookie 验证的核心。它的作用是:
- 解析 Cookie 的值。
- 验证 Cookie 的有效性(比如,是否过期,是否被篡改)。
- 如果 Cookie 有效,返回用户 ID;否则,返回 0。
function wp_validate_auth_cookie( $cookie = '', $scheme = '' ) {
if ( ! $cookie ) {
if ( ! empty( $_COOKIE[ LOGGED_IN_COOKIE ] ) ) {
$cookie = $_COOKIE[ LOGGED_IN_COOKIE ];
$scheme = 'logged_in';
} elseif ( ! empty( $_COOKIE[ SECURE_AUTH_COOKIE ] ) && is_ssl() ) {
$cookie = $_COOKIE[ SECURE_AUTH_COOKIE ];
$scheme = 'secure';
} elseif ( ! empty( $_COOKIE[ AUTH_COOKIE ] ) ) {
$cookie = $_COOKIE[ AUTH_COOKIE ];
$scheme = 'auth';
} else {
return false;
}
}
$cookie_elements = explode( '|', $cookie );
if ( count( $cookie_elements ) !== 4 ) {
return false;
}
list( $username, $expiration, $token, $hmac ) = $cookie_elements;
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
$username = preg_replace( '/[^a-zA-Z0-9_.@-]/', '', $username );
$expiration = preg_replace( '/[^0-9]/', '', $expiration );
}
/**
* Filter the user data used to validate the auth cookie.
*
* @since 2.5.0
*
* @param array $user_data Array of user data. Contains the username,
* expiration, token, and the HMAC from the cookie.
* @param string $cookie The cookie value.
* @param string $scheme The scheme to use. 'auth', 'secure', 'logged_in'
*/
$user_data = apply_filters( 'auth_cookie_validation', compact( 'username', 'expiration', 'token', 'hmac' ), $cookie, $scheme );
$username = $user_data['username'];
$expiration = $user_data['expiration'];
$token = $user_data['token'];
$hmac = $user_data['hmac'];
if ( empty( $username ) || ! is_numeric( $expiration ) ) {
return false;
}
// Quick check to see if an honest cookie has expired.
if ( $expiration < time() ) {
return false;
}
$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, $scheme );
$hash = hash_hmac( 'md5', $username . '|' . $expiration . '|' . $token, $key );
if ( ! hash_equals( $hmac, $hash ) ) {
/**
* Fires after an invalid auth cookie is detected.
*
* @since 4.4.0
*
* @param WP_User $user User object.
*/
do_action( 'auth_cookie_malformed', $user );
return false;
}
return $user->ID;
}
这个函数稍微有点复杂,咱们拆解一下:
- 获取 Cookie 值: 如果没有传入 Cookie 值,就尝试从
$_COOKIE
数组中获取。 - 分割 Cookie 值: Cookie 值通常由四个部分组成,用
|
分隔:username|expiration|token|hmac
。 - 数据清洗(DEBUG 模式): 如果开启了
WP_DEBUG
,会对username
和expiration
进行简单的清理,确保只包含特定字符。 - 过滤 (Hook): 使用
auth_cookie_validation
钩子,允许插件或主题修改 Cookie 中的数据。 - 基本校验: 检查
username
和expiration
是否为空,以及expiration
是否已过期。 - 获取用户: 根据
username
从数据库中获取用户信息。 - 生成密钥: 使用用户名、密码片段、过期时间和 Token,生成一个密钥。
- 生成 HMAC: 使用密钥和原始数据,生成一个 HMAC (Hash-based Message Authentication Code)。HMAC 用于验证数据的完整性,防止被篡改。
- 比较 HMAC: 将 Cookie 中的 HMAC 和生成的 HMAC 进行比较。如果不一样,说明 Cookie 被篡改了,验证失败。
- 返回用户 ID: 如果所有验证都通过了,就返回用户的 ID。
Cookie 的构成:
组成部分 | 含义 | 备注 |
---|---|---|
username | 用户名 | 用于查找用户 |
expiration | 过期时间戳 | 用于判断 Cookie 是否过期 |
token | 安全令牌 | 用于增加安全性,防止 Cookie 被盗用后长期有效 |
hmac | HMAC (Hash-based Message Authentication Code),一种消息认证码,通过 Hash 函数和密钥对消息进行加密生成,用于验证数据的完整性。 | 用于验证 Cookie 的完整性,防止被篡改。 如果 HMAC 不匹配,说明 Cookie 的内容可能被修改过。 |
Cookie 的种类:
WordPress 使用三种主要的 Cookie 来进行身份验证:
LOGGED_IN_COOKIE
:用于记住已登录的用户。SECURE_AUTH_COOKIE
:用于 HTTPS 连接,提供更安全的身份验证。AUTH_COOKIE
:用于非 HTTPS 连接,安全性较低。
四、会话(Session)的秘密:is_user_logged_in()
和 wp_get_current_user()
如果 Cookie 验证失败,determine_current_user()
函数会尝试从会话中获取用户信息。这里涉及到两个函数:
is_user_logged_in()
:检查用户是否已经登录(即,会话中是否存在用户信息)。wp_get_current_user()
:从会话中获取当前用户信息。
function is_user_logged_in() {
$user = wp_get_current_user();
return ( $user->exists() );
}
function wp_get_current_user() {
global $current_user;
if ( ! ( $current_user instanceof WP_User ) ) {
$current_user = new WP_User();
if ( isset( $_SESSION ) && isset( $_SESSION['wp_user_id'] ) && is_numeric( $_SESSION['wp_user_id'] ) ) {
$user = get_userdata( $_SESSION['wp_user_id'] );
if ( $user ) {
$current_user = $user;
}
}
}
return $current_user;
}
is_user_logged_in()
函数很简单,它只是调用 wp_get_current_user()
函数,然后检查返回的用户对象是否存在。
wp_get_current_user()
函数稍微复杂一点:
- 检查
$current_user
: 首先检查全局变量$current_user
是否已经是一个WP_User
对象。如果是,直接返回。 - 创建
$current_user
: 如果$current_user
不是WP_User
对象,就创建一个新的WP_User
对象。 - 从会话中获取用户 ID: 检查
$_SESSION
数组中是否存在wp_user_id
键。如果存在,就从数据库中获取用户信息,并赋值给$current_user
。 - 返回
$current_user
: 返回$current_user
对象。
会话的流程:
- 用户登录: 当用户成功登录时,WordPress 会将用户的 ID 存储到
$_SESSION['wp_user_id']
中。 - 后续请求: 在后续的请求中,
wp_get_current_user()
函数会从$_SESSION
中获取用户 ID,并加载用户信息。 - 用户注销: 当用户注销时,WordPress 会销毁会话,或者至少删除
$_SESSION['wp_user_id']
键。
五、钩子(Hooks)的妙用
determine_current_user()
函数在执行前后,分别调用了 do_action()
函数,触发了两个钩子:
determine_current_user
:在函数开始执行前触发。after_determine_current_user
:在函数执行结束后触发。
这两个钩子允许插件或主题在用户身份验证过程中,插入自定义的逻辑。比如:
- 可以添加自定义的 Cookie 验证机制。
- 可以根据用户的 IP 地址或浏览器信息,进行额外的身份验证。
- 可以在用户登录后,执行一些自定义的操作。
六、总结:用户身份验证的流程
让我们总结一下 determine_current_user()
函数的工作流程:
- 检查是否已确定用户: 如果
$current_user->ID
不为空,直接返回。 - Cookie 验证:
- 尝试从
LOGGED_IN_COOKIE
、SECURE_AUTH_COOKIE
和AUTH_COOKIE
中获取 Cookie 值。 - 调用
wp_validate_auth_cookie()
函数验证 Cookie。 - 如果 Cookie 验证成功,设置当前用户。
- 尝试从
- 会话验证:
- 如果 Cookie 验证失败,检查用户是否已经登录(
is_user_logged_in()
)。 - 如果用户已经登录,从会话中获取用户信息(
wp_get_current_user()
)。
- 如果 Cookie 验证失败,检查用户是否已经登录(
- 钩子: 在函数执行前后,触发钩子,允许插件或主题扩展功能。
七、实战:模拟用户登录流程
为了更好地理解 determine_current_user()
函数的工作原理,我们可以模拟一个简单的用户登录流程:
- 用户提交登录信息: 用户在登录表单中输入用户名和密码。
- 验证用户: 服务器验证用户名和密码是否正确。
- 设置 Cookie: 如果验证成功,服务器生成一个包含用户信息的 Cookie,并发送给客户端。
- 后续请求: 在后续的请求中,客户端会将 Cookie 发送给服务器。
determine_current_user()
函数: 服务器端的determine_current_user()
函数会解析 Cookie,验证用户身份,并设置当前用户。
八、安全注意事项
用户身份验证是个非常重要的安全环节,需要注意以下几点:
- 使用 HTTPS: 确保网站使用 HTTPS 协议,防止 Cookie 被窃听。
- 保护 Cookie: 设置 Cookie 的
HttpOnly
和Secure
标志,防止 Cookie 被 JavaScript 访问和在非 HTTPS 连接中传输。 - 定期更换密钥: 定期更换用于生成 HMAC 的密钥,防止 Cookie 被破解。
- 防止暴力破解: 限制登录尝试次数,防止暴力破解密码。
- 使用强密码: 强制用户使用强密码,增加密码破解的难度。
- 及时更新 WordPress: 及时更新 WordPress 和插件,修复安全漏洞。
九、总结的总结
determine_current_user()
函数是 WordPress 用户身份验证的核心,它通过 Cookie 和会话信息,识别出当前用户。 理解了这个函数的工作原理,可以更好地理解 WordPress 的用户身份验证机制,并为开发更安全、更可靠的 WordPress 插件和主题打下基础。
好了,今天的讲座就到这里。 希望大家有所收获! 如果有什么问题,欢迎提问。下次再见!