feat(security): 新增网站安全扩展模块并支持修改手机脱敏验证

This commit is contained in:
kate522 committed 2026-10-06 09:26:55 +08:00
1 parent 224f86ec08
commit af72364078
30 files changed
+1389 -73

No files matched your search

+68
View File
@@ -0,0 +1,68 @@
<?php
/**
* Phone number masking helpers shared by the account UI and the
* phone-match verification endpoint.
*
* The hidden middle segment adapts to the number: domestic 11-digit
* numbers hide 4 digits (138****1234), longer international numbers
* hide length-7 digits (min 4), so the digit-entry UI renders one box
* per hidden digit.
*
* @package AKSRT
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* Split a bound phone number into visible prefix / hidden middle / visible suffix.
*
* Works on the digit core of the number (a leading "+" is ignored for the split
* but reported back). Returns null when the number holds fewer than 8 digits —
* such numbers cannot be verified by middle-digit entry.
*
* @return array{prefix:string,hidden:string,suffix:string,hidden_length:int,leading_plus:bool}|null
*/
function aksrt_phone_mask_parts(string $phone): ?array {
$phone = trim($phone);
if ($phone === '') {
return null;
}
$leading_plus = $phone[0] === '+';
$core = preg_replace('/\D+/', '', $phone);
$length = strlen($core);
if ($length < 8) {
return null;
}
$hidden_length = min(11, max(4, $length - 7));
$suffix_length = $length - 3 - $hidden_length;
if ($suffix_length < 1) {
return null;
}
return [
'prefix' => substr($core, 0, 3),
'hidden' => substr($core, 3, $hidden_length),
'suffix' => substr($core, $length - $suffix_length),
'hidden_length' => $hidden_length,
'leading_plus' => $leading_plus,
];
}
/**
* Masked display form of a phone number, e.g. 138****1234 or +861******1234.
* Falls back to the raw number when it cannot be masked.
*/
function aksrt_phone_mask_display(string $phone): string {
$parts = aksrt_phone_mask_parts($phone);
if ($parts === null) {
return $phone;
}
$mask = str_repeat('*', (int) $parts['hidden_length']);
$display = $parts['prefix'] . $mask . $parts['suffix'];
return $parts['leading_plus'] ? '+' . $display : $display;
}
@@ -11,7 +11,7 @@ $account_user = isset($target_user) && $target_user instanceof WP_User ? $target
$account_user_id = $account_user instanceof WP_User ? (int) $account_user->ID : (int) ($user_id ?? 0);
$account_email = $account_user instanceof WP_User ? (string) $account_user->user_email : '';
$account_phone = $account_user_id > 0 ? (string) get_user_meta($account_user_id, 'phone_number', true) : '';
$account_phone_display = strlen($account_phone) > 10 ? substr_replace($account_phone, '****', 3, 4) : $account_phone;
$account_phone_display = function_exists('aksrt_phone_mask_display') ? aksrt_phone_mask_display($account_phone) : $account_phone;
$account_oauth_new = $account_user_id > 0 && get_user_meta($account_user_id, 'oauth_new', true);
$user_settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
$sms_settings = class_exists('\AKSRT\Sms\SMS') ? \AKSRT\Sms\SMS::get_instance()->get_provider_config() : [];
@@ -147,7 +147,29 @@ $render_phone_form = static function () use ($agreement): void {
<?php if ($account_phone !== '') : ?>
<ul class="mb20 step-simple account-step-list" data-account-step-list="phone"><li class="active"><span><?php esc_html_e('验证老手机', 'aksrt'); ?></span></li><li><span><?php esc_html_e('验证新手机', 'aksrt'); ?></span></li><li><span><?php esc_html_e('修改成功', 'aksrt'); ?></span></li></ul>
<div class="account-step-panels">
<div class="account-step is-active" data-account-step="phone-old"><p class="muted-2-color"><?php printf(esc_html__('请在下方获取验证码以验证您的老手机号 %s', 'aksrt'), '<br><span class="badg">' . esc_html($account_phone_display) . '</span>'); ?></p><form id="phone-old-verify-form"><input type="hidden" name="phone" value="<?php echo esc_attr($account_phone); ?>"><div class="mb20"><?php if (function_exists('aksrt_render_captcha')) : ?><?php aksrt_render_captcha('bind_phone'); ?><?php endif; ?><div class="relative line-form mb10 modal-line-field modal-line-field--code"><input type="text" name="code" class="line-form-input" maxlength="6" inputmode="numeric" autocomplete="one-time-code" required><div class="scale-placeholder"><?php esc_html_e('验证码', 'aksrt'); ?></div><span class="yztx abs-right"><button type="button" class="but c-blue code-send-btn account-code-send" data-aksrt-sms-trigger data-aksrt-sms-scene="verify_old_phone"><?php esc_html_e('发送验证码', 'aksrt'); ?></button></span><i class="line-form-line"></i></div></div><button type="submit" class="but jb-blue padding-lg btn-block"><i class="fa fas fa-shield-alt" aria-hidden="true"></i> <?php esc_html_e('立即验证', 'aksrt'); ?></button></form></div>
<div class="account-step is-active" data-account-step="phone-old">
<?php $phone_mask = function_exists('aksrt_phone_mask_parts') ? aksrt_phone_mask_parts($account_phone) : null; ?>
<?php if ($phone_mask !== null) : ?>
<p class="muted-2-color"><?php printf(esc_html__('为确认是您本人操作,请补全绑定手机号 %s 中缺失的数字', 'aksrt'), '<br><span class="badg">' . esc_html($account_phone_display) . '</span>'); ?></p>
<?php else : ?>
<p class="muted-2-color"><?php printf(esc_html__('请在下方获取验证码以验证您的老手机号 %s', 'aksrt'), '<br><span class="badg">' . esc_html($account_phone_display) . '</span>'); ?></p>
<?php endif; ?>
<form id="phone-old-verify-form" novalidate>
<?php if ($phone_mask !== null) : ?>
<div class="mb20 aksrt-phone-digits" data-aksrt-phone-digits data-hidden-length="<?php echo esc_attr((string) $phone_mask['hidden_length']); ?>" role="group" aria-label="<?php esc_attr_e('请补全手机号中缺失的数字', 'aksrt'); ?>">
<?php for ($phone_digit_index = 0; $phone_digit_index < (int) $phone_mask['hidden_length']; $phone_digit_index++) : ?>
<input type="text" class="aksrt-phone-digit" inputmode="numeric" maxlength="1" autocomplete="one-time-code" data-digit-index="<?php echo esc_attr((string) $phone_digit_index); ?>" aria-label="<?php echo esc_attr(sprintf(__('手机号第 %d 位数字', 'aksrt'), 4 + $phone_digit_index)); ?>">
<?php endfor; ?>
</div>
<p class="aksrt-phone-digits__error" data-aksrt-phone-digits-error hidden></p>
<?php endif; ?>
<div class="mb20" data-aksrt-phone-code-area<?php echo $phone_mask !== null ? ' hidden' : ''; ?>>
<?php if (function_exists('aksrt_render_captcha')) : ?><?php aksrt_render_captcha('bind_phone'); ?><?php endif; ?>
<div class="relative line-form mb10 modal-line-field modal-line-field--code"><input type="text" name="code" class="line-form-input" maxlength="6" inputmode="numeric" autocomplete="one-time-code" required><div class="scale-placeholder"><?php esc_html_e('验证码', 'aksrt'); ?></div><span class="yztx abs-right"><button type="button" class="but c-blue code-send-btn account-code-send" data-aksrt-sms-trigger data-aksrt-sms-scene="verify_old_phone"><?php esc_html_e('发送验证码', 'aksrt'); ?></button></span><i class="line-form-line"></i></div>
<button type="submit" class="but jb-blue padding-lg btn-block"><i class="fa fas fa-shield-alt" aria-hidden="true"></i> <?php esc_html_e('立即验证', 'aksrt'); ?></button>
</div>
</form>
</div>
<div class="account-step" data-account-step="phone-new"><p class="muted-2-color"><?php esc_html_e('请输入您需要修改的新手机号码', 'aksrt'); ?></p><?php $render_phone_form(); ?></div>
<div class="account-step" data-account-step="phone-success"><div class="text-center padding-h10"><i class="fa fas fa-check-circle c-green em3x mb10" aria-hidden="true"></i><p><?php esc_html_e('手机号修改成功', 'aksrt'); ?></p></div></div>
</div>