各位观众老爷们,大家好!今天咱们来聊聊 WordPress 登录背后的男人—— wp_signon()
函数。别看它名字平平无奇,但它可是 WordPress 登录的灵魂人物,负责验证你的身份,然后给你发个“通行证”(Cookie),让你在 WordPress 的世界里畅行无阻。
咱们今天就来扒一扒它的源码,看看它是怎么做到这一切的。请各位坐稳扶好,咱们发车啦!
一、 wp_signon()
的身世背景
wp_signon()
函数位于 wp-includes/pluggable.php
文件中,它的主要作用就是:
- 验证用户名和密码: 看看你提供的用户名和密码是否正确,是否与数据库中的记录匹配。
- 设置登录 Cookie: 如果验证通过,就设置登录 Cookie,让 WordPress 记住你的身份。
- 返回用户信息: 如果验证成功,返回用户信息对象,方便你在其他地方使用。
二、源码剖析:一行一行地扒它的皮
咱们先来看看 wp_signon()
函数的庐山真面目(简化版,去掉了部分不常用的参数和逻辑):
function wp_signon( $credentials = array(), $secure_cookie = '' ) {
/**
* Fires before the user is signed on.
*
* @since 2.5.0
*
* @param array $credentials An array of user signon credentials.
* @param bool $secure_cookie Whether the session is using a secure cookie.
*/
do_action( 'wp_signon', $credentials, $secure_cookie );
$secure_cookie = apply_filters( 'secure_signon_cookie', $secure_cookie, $credentials );
$user = wp_authenticate( $credentials['user_login'], $credentials['user_password'] );
if ( is_wp_error( $user ) ) {
return $user;
}
wp_set_auth_cookie( $user->ID, false, $secure_cookie );
do_action( 'wp_login', $user->user_login, $user );
return $user;
}
别害怕,代码不多,咱们一步一步来。
-
do_action( 'wp_signon', $credentials, $secure_cookie );
这是个“钩子”(Hook),允许其他插件或主题在用户登录之前执行一些操作。你可以理解为,
wp_signon()
函数在登录之前,先问问大家:“还有谁要插一脚吗?”。 -
$secure_cookie = apply_filters( 'secure_signon_cookie', $secure_cookie, $credentials );
这又是个“钩子”,允许修改
secure_cookie
的值。secure_cookie
决定了登录 Cookie 是否只能通过 HTTPS 传输。一般来说,如果你的网站启用了 HTTPS,就应该设置为true
,以保证安全。 -
$user = wp_authenticate( $credentials['user_login'], $credentials['user_password'] );
这才是重头戏!
wp_authenticate()
函数负责验证用户名和密码。它会检查数据库,看看你提供的用户名和密码是否匹配。如果匹配,就返回用户信息对象;如果不匹配,就返回一个 WP_Error 对象,告诉你登录失败的原因。咱们再深入
wp_authenticate()
函数看看:function wp_authenticate( $username, $password ) { $username = sanitize_user( $username ); $password = trim( $password ); /** * Fires before a user is authenticated. * * @since 2.5.0 * * @param string $username Username passed to wp_authenticate. * @param string $password Password passed to wp_authenticate. */ do_action( 'wp_authenticate', $username, $password ); $errors = new WP_Error(); if ( empty( $username ) ) { $errors->add( 'empty_username', __( '<strong>错误:</strong> 请输入用户名。' ) ); } elseif ( ! validate_username( $username ) ) { $errors->add( 'invalid_username', __( '<strong>错误:</strong> 无效的用户名。' ) ); $username = ''; } if ( empty( $password ) ) { $errors->add( 'empty_password', __( '<strong>错误:</strong> 请输入密码。' ) ); } if ( $errors->has_errors() ) { return $errors; } $user = get_user_by( 'login', $username ); if ( ! $user ) { /** * Fires after a user fails authentication. * * @since 2.1.0 * * @param string $username Username passed to wp_authenticate. * @param string $password Password passed to wp_authenticate. * @param WP_Error $errors A WP_Error object. */ do_action( 'wp_login_failed', $username, $password, $errors ); $errors->add( 'invalid_username', __( '<strong>错误:</strong> 用户名错误。' ) ); return $errors; } $user = apply_filters( 'wp_authenticate_user', $user, $password ); if ( is_wp_error( $user ) ) { /** * Fires after a user fails authentication. * * @since 2.1.0 * * @param string $username Username passed to wp_authenticate. * @param string $password Password passed to wp_authenticate. * @param WP_Error $errors A WP_Error object. */ do_action( 'wp_login_failed', $username, $password, $user ); return $user; } if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) { /** * Fires after a user fails authentication. * * @since 2.1.0 * * @param string $username Username passed to wp_authenticate. * @param string $password Password passed to wp_authenticate. * @param WP_Error $errors A WP_Error object. */ do_action( 'wp_login_failed', $username, $password, $errors ); $errors->add( 'incorrect_password', __( '<strong>错误:</strong> 密码错误。' ) ); return $errors; } wp_cache_delete( $user->ID, 'user_meta' ); return $user; }
wp_authenticate()
函数的逻辑如下:- 数据清理: 使用
sanitize_user()
清理用户名,使用trim()
去除密码两端的空格。 - 空值检查: 检查用户名和密码是否为空。如果为空,就返回错误。
- 用户名验证: 使用
validate_username()
验证用户名是否有效。 - 获取用户信息: 使用
get_user_by( 'login', $username )
根据用户名从数据库中获取用户信息。 - 用户是否存在: 如果用户不存在,就返回错误。
- 密码验证: 使用
wp_check_password()
验证密码是否正确。这是个关键函数,咱们待会儿重点讲。 - 清除缓存: 使用
wp_cache_delete()
清除用户元数据的缓存。 - 返回用户信息: 如果一切顺利,就返回用户信息对象。
咱们重点关注一下
wp_check_password()
函数:function wp_check_password( $password, $hash, $user_id = '' ) { global $wp_hasher; if ( empty( $wp_hasher ) ) { require_once ABSPATH . WPINC . '/class-phpass.php'; $wp_hasher = new PasswordHash( 8, true ); } $check = $wp_hasher->CheckPassword( $password, $hash ); /** * Filters whether the given password is valid. * * @since 2.5.0 * * @param bool $check Whether the password is valid. * @param string $password The password to check. * @param string $hash The stored password hash. * @param int $user_id User ID. */ return apply_filters( 'check_password', $check, $password, $hash, $user_id ); }
wp_check_password()
函数使用了PasswordHash
类来验证密码。PasswordHash
类使用了 bcrypt 算法来对密码进行哈希处理,保证密码的安全性。简单来说,
wp_check_password()
函数会把用户输入的密码也用 bcrypt 算法进行哈希处理,然后和数据库中存储的哈希值进行比较。如果相同,就说明密码正确;否则,密码错误。 - 数据清理: 使用
-
if ( is_wp_error( $user ) ) { return $user; }
如果
wp_authenticate()
函数返回了一个 WP_Error 对象,就说明登录失败了,wp_signon()
函数会直接返回这个错误对象。 -
wp_set_auth_cookie( $user->ID, false, $secure_cookie );
如果
wp_authenticate()
函数返回了用户信息对象,就说明登录成功了,wp_signon()
函数会调用wp_set_auth_cookie()
函数来设置登录 Cookie。咱们再深入
wp_set_auth_cookie()
函数看看:function wp_set_auth_cookie( $user_id, $remember = false, $secure = '' ) { if ( empty( $secure ) ) { $secure = is_ssl(); } $expiration = time() + apply_filters( 'auth_cookie_expiration', ( $remember ? 3024000 : 1209600 ), $user_id, $remember ); $cookie = wp_generate_auth_cookie( $user_id, $expiration, 'auth', $secure ); do_action( 'set_auth_cookie', $cookie, $user_id, $expiration, 'auth', $remember, $secure ); setcookie( AUTH_COOKIE, $cookie, $expiration, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN, $secure, true ); if ( COOKIEPATH != SITECOOKIEPATH ) { setcookie( AUTH_COOKIE, $cookie, $expiration, SITECOOKIEPATH, COOKIE_DOMAIN, $secure, true ); } if ( $remember ) { wp_set_remember_cookie( $user_id, $expiration, $secure ); } /** * Fires after the authentication cookie is set. * * @since 3.5.0 * * @param string $cookie The authentication cookie. * @param int $user_id User ID. * @param int $expiration The time the cookie expires. * @param string $scheme Authentication scheme. Default 'auth'. */ do_action( 'auth_cookie_set', $cookie, $user_id, $expiration, 'auth' ); }
wp_set_auth_cookie()
函数的逻辑如下:- 确定是否安全: 如果没有指定
secure
,就根据当前是否是 HTTPS 来确定。 - 计算过期时间: 根据是否选择了“记住我”来计算 Cookie 的过期时间。
- 生成 Cookie: 使用
wp_generate_auth_cookie()
函数生成 Cookie 的值。 - 设置 Cookie: 使用
setcookie()
函数设置 Cookie。 - 设置“记住我”Cookie: 如果选择了“记住我”,就调用
wp_set_remember_cookie()
函数来设置“记住我”Cookie。
咱们再深入
wp_generate_auth_cookie()
函数看看:function wp_generate_auth_cookie( $user_id, $expiration, $scheme = 'auth', $token = '' ) { $pass_frag = substr( wp_hash( get_user_meta( $user_id, 'session_tokens', true ) . '|' . get_user_meta( $user_id, 'pass', true ) ), 8, 4 ); $key = wp_hash( $user_id . '|' . $pass_frag . '|' . $expiration . '|' . $scheme . '|' . $token ); $hash = hash_hmac( 'md5', $user_id . '|' . $expiration . '|' . $scheme . '|' . $token, $key ); $cookie = $user_id . '|' . $expiration . '|' . $hash . '|' . $scheme . '|' . $token; /** * Filters the authentication cookie. * * @since 2.6.0 * * @param string $cookie Authentication cookie. * @param int $user_id User ID. * @param int $expiration The time the cookie expires. * @param string $scheme Authentication scheme. Default 'auth'. */ return apply_filters( 'auth_cookie', $cookie, $user_id, $expiration, $scheme ); }
wp_generate_auth_cookie()
函数会生成一个包含用户 ID、过期时间、哈希值、登录方案和令牌的字符串,作为 Cookie 的值。这个哈希值是通过一系列哈希算法生成的,保证 Cookie 的安全性。至此,咱们就完成了登录 Cookie 的设置。
- 确定是否安全: 如果没有指定
-
do_action( 'wp_login', $user->user_login, $user );
这是个“钩子”,允许其他插件或主题在用户登录之后执行一些操作。你可以理解为,
wp_signon()
函数在登录之后,再问问大家:“还有谁要庆祝一下吗?”。 -
return $user;
wp_signon()
函数最终会返回用户信息对象,方便你在其他地方使用。
三、总结:wp_signon()
的工作流程
咱们用一张表格来总结一下 wp_signon()
函数的工作流程:
步骤 | 函数/操作 | 说明 |
---|---|---|
1 | do_action( 'wp_signon' ) |
登录前钩子,允许插件或主题在登录之前执行操作。 |
2 | apply_filters( 'secure_signon_cookie' ) |
允许修改 secure_cookie 的值,决定登录 Cookie 是否只能通过 HTTPS 传输。 |
3 | wp_authenticate() |
验证用户名和密码。 |
4 | sanitize_user() |
清理用户名。 |
5 | trim() |
去除密码两端的空格。 |
6 | 空值检查 | 检查用户名和密码是否为空。 |
7 | validate_username() |
验证用户名是否有效。 |
8 | get_user_by( 'login', $username ) |
根据用户名从数据库中获取用户信息。 |
9 | 用户是否存在 | 检查用户是否存在。 |
10 | wp_check_password() |
验证密码是否正确。使用 bcrypt 算法对密码进行哈希处理,并与数据库中的哈希值进行比较。 |
11 | wp_cache_delete() |
清除用户元数据的缓存。 |
12 | wp_set_auth_cookie() |
设置登录 Cookie。 |
13 | wp_generate_auth_cookie() |
生成 Cookie 的值,包含用户 ID、过期时间、哈希值、登录方案和令牌。 |
14 | setcookie() |
设置 Cookie。 |
15 | wp_set_remember_cookie() |
如果选择了“记住我”,就设置“记住我”Cookie。 |
16 | do_action( 'wp_login' ) |
登录后钩子,允许插件或主题在登录之后执行操作。 |
17 | return $user |
返回用户信息对象。 |
四、安全性考量:wp_signon()
如何保护你的账户
- 密码哈希: 使用 bcrypt 算法对密码进行哈希处理,保证密码的安全性。即使数据库泄露,攻击者也无法直接获取用户的明文密码。
- Cookie 安全: 生成 Cookie 时,使用了哈希算法,防止 Cookie 被篡改。
- HTTPS 支持: 可以通过
secure_cookie
参数来强制使用 HTTPS 传输 Cookie,防止 Cookie 被窃听。 - 钩子机制: 提供了大量的钩子,允许开发者自定义登录流程,增强安全性。
五、总结与展望
wp_signon()
函数是 WordPress 登录的核心,它负责验证用户身份,并设置登录 Cookie。通过深入了解它的源码,我们可以更好地理解 WordPress 的登录机制,并可以根据自己的需求进行定制和扩展。
当然,WordPress 的登录机制还有很多细节,例如“记住我”功能、密码重置功能等等。咱们今天就先聊到这里,以后有机会再深入探讨。
希望今天的讲座对大家有所帮助!谢谢大家!