192 lines
5.7 KiB
PHP
192 lines
5.7 KiB
PHP
<?php
|
|
|
|
/**
|
|
* 文件功能:找回密码账号检测与防扫描轰炸限流 Feature 测试
|
|
*
|
|
* 覆盖密码重置流程中的多维限流阀门,确保 IP 防扫与用户邮箱防轰炸策略稳健起效。
|
|
*
|
|
* @author ChatRoom Laravel
|
|
*
|
|
* @version 1.0.0
|
|
*/
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Sysparam;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\RateLimiter;
|
|
use Tests\TestCase;
|
|
|
|
class PasswordResetRateLimitTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
RateLimiter::clear('pw-check:ip:127.0.0.1');
|
|
RateLimiter::clear('pw-email:ip:127.0.0.1');
|
|
}
|
|
|
|
/**
|
|
* 测试账号检测:不存在账号返回 404/not_found
|
|
*/
|
|
public function test_check_account_returns_not_found_when_user_does_not_exist(): void
|
|
{
|
|
$response = $this->postJson(route('password.check_account'), [
|
|
'username' => 'non_existing_user_abc',
|
|
]);
|
|
|
|
$response->assertStatus(200);
|
|
$response->assertJson([
|
|
'status' => 'not_found',
|
|
'message' => '抱歉,没有找到该昵称对应的账号。请确认后再试。',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 测试账号检测分流状态与脱敏邮箱输出
|
|
*/
|
|
public function test_check_account_shows_correct_channels(): void
|
|
{
|
|
// 1. 双绑定用户
|
|
$bothBoundUser = User::factory()->create([
|
|
'username' => 'both_user',
|
|
'email' => 'both.test@example.com',
|
|
'wxid' => 'wxid_both',
|
|
]);
|
|
|
|
$response1 = $this->postJson(route('password.check_account'), [
|
|
'username' => 'both_user',
|
|
]);
|
|
|
|
$response1->assertJson([
|
|
'status' => 'success',
|
|
'has_email' => true,
|
|
'has_wechat' => true,
|
|
'masked_email' => 'b*******t@example.com',
|
|
]);
|
|
|
|
// 2. 仅绑定微信用户
|
|
$wechatUser = User::factory()->create([
|
|
'username' => 'wx_user',
|
|
'email' => null,
|
|
'wxid' => 'wxid_single',
|
|
]);
|
|
|
|
$response2 = $this->postJson(route('password.check_account'), [
|
|
'username' => 'wx_user',
|
|
]);
|
|
|
|
$response2->assertJson([
|
|
'status' => 'success',
|
|
'has_email' => false,
|
|
'has_wechat' => true,
|
|
]);
|
|
|
|
// 3. 均未绑定用户
|
|
$noneUser = User::factory()->create([
|
|
'username' => 'none_user',
|
|
'email' => null,
|
|
'wxid' => null,
|
|
]);
|
|
|
|
$response3 = $this->postJson(route('password.check_account'), [
|
|
'username' => 'none_user',
|
|
]);
|
|
|
|
$response3->assertJson([
|
|
'status' => 'success',
|
|
'has_email' => false,
|
|
'has_wechat' => false,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 测试 IP 防扫描限流限制 (每个 IP 1分钟限制 5 次检测)
|
|
*/
|
|
public function test_check_account_rate_limiting(): void
|
|
{
|
|
$ip = '127.0.0.1';
|
|
|
|
// 连续请求 5 次都应该正常响应
|
|
for ($i = 0; $i < 5; $i++) {
|
|
$response = $this->postJson(route('password.check_account'), [
|
|
'username' => 'non_existing_user',
|
|
]);
|
|
$response->assertStatus(200);
|
|
}
|
|
|
|
// 第 6 次应该被 RateLimiter 节流拦截返回 429
|
|
$response = $this->postJson(route('password.check_account'), [
|
|
'username' => 'non_existing_user',
|
|
]);
|
|
|
|
$response->assertStatus(429);
|
|
$response->assertJson([
|
|
'status' => 'error',
|
|
]);
|
|
$this->assertStringContainsString('请求过于频繁', $response->json('message'));
|
|
}
|
|
|
|
/**
|
|
* 测试当用户输入的邮箱地址与绑定的真实邮箱不匹配时,二次核验拦截报错
|
|
*/
|
|
public function test_store_link_fails_when_masked_email_mismatch(): void
|
|
{
|
|
Sysparam::updateOrCreate(['alias' => 'smtp_enabled'], ['body' => '1']);
|
|
|
|
User::factory()->create([
|
|
'username' => 'mismatch_user',
|
|
'email' => 'real.mail@example.com',
|
|
]);
|
|
|
|
$response = $this->postJson(route('password.email'), [
|
|
'username' => 'mismatch_user',
|
|
'email' => 'wrong.mail@example.com', // 错误的手输邮箱
|
|
]);
|
|
|
|
$response->assertStatus(422);
|
|
$response->assertJson([
|
|
'status' => 'error',
|
|
'message' => '输入的完整邮箱地址与该账号绑定的邮箱不一致,二次确认失败。',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 测试发送链接的双层安全控频防御 (防止发信轰炸他人)
|
|
*/
|
|
public function test_store_link_prevents_email_bombing(): void
|
|
{
|
|
Sysparam::updateOrCreate(['alias' => 'smtp_enabled'], ['body' => '1']);
|
|
|
|
$user = User::factory()->create([
|
|
'username' => 'bomb_user',
|
|
'email' => 'bomb.target@example.com',
|
|
]);
|
|
|
|
$email = $user->email;
|
|
$targetKey = 'pw-email:target:'.md5($email);
|
|
RateLimiter::clear($targetKey);
|
|
|
|
// 模拟第一次发送
|
|
$response1 = $this->postJson(route('password.email'), [
|
|
'username' => 'bomb_user',
|
|
'email' => 'bomb.target@example.com',
|
|
]);
|
|
|
|
// 第二次在 3 分钟内再次连续发送,触发 429 节流
|
|
$response2 = $this->postJson(route('password.email'), [
|
|
'username' => 'bomb_user',
|
|
'email' => 'bomb.target@example.com',
|
|
]);
|
|
|
|
$response2->assertStatus(429);
|
|
$response2->assertJson([
|
|
'status' => 'error',
|
|
]);
|
|
$this->assertStringContainsString('重置链接过于频繁', $response2->json('message'));
|
|
}
|
|
}
|