完善首页邮箱找回密码流程
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:前台邮箱找回密码控制器
|
||||
*
|
||||
* 提供独立的找回密码页、发送邮箱重置链接、展示重置页以及提交新密码功能。
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\ResetPasswordRequest;
|
||||
use App\Http\Requests\SendPasswordResetLinkRequest;
|
||||
use App\Models\Sysparam;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Events\PasswordReset;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* 类功能:处理首页邮箱找回密码的完整流程。
|
||||
*/
|
||||
class PasswordResetController extends Controller
|
||||
{
|
||||
/**
|
||||
* 展示独立的邮箱找回密码页面。
|
||||
*/
|
||||
public function create(): View
|
||||
{
|
||||
return view('password-forgot', [
|
||||
'systemName' => Sysparam::where('alias', 'sys_name')->value('body') ?? '和平聊吧',
|
||||
'smtpEnabled' => $this->isPasswordResetMailEnabled(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮箱找回密码链接。
|
||||
*/
|
||||
public function storeLink(SendPasswordResetLinkRequest $request): JsonResponse
|
||||
{
|
||||
if (! $this->isPasswordResetMailEnabled()) {
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => '系统暂未开启邮箱发信服务,当前无法通过邮箱找回密码。',
|
||||
], 403);
|
||||
}
|
||||
|
||||
$email = trim((string) $request->string('email'));
|
||||
|
||||
// 邮箱找回必须保证一邮一号,否则重置目标会产生歧义。
|
||||
if (User::query()->where('email', $email)->count() > 1) {
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => '该邮箱绑定了多个账号,暂不支持自助找回,请联系管理员处理。',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$status = Password::sendResetLink(['email' => $email]);
|
||||
|
||||
if ($status === Password::RESET_LINK_SENT) {
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => '如果该邮箱已绑定账号,系统已发送重置邮件。链接 60 分钟内有效,请注意查收。',
|
||||
]);
|
||||
}
|
||||
|
||||
if ($status === Password::RESET_THROTTLED) {
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => '发送过于频繁,请稍后再试。',
|
||||
], 429);
|
||||
}
|
||||
|
||||
if ($status === Password::INVALID_USER) {
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => '如果该邮箱已绑定账号,系统已发送重置邮件。请检查收件箱与垃圾邮件箱。',
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => '找回密码邮件发送失败,请稍后重试。',
|
||||
], 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示独立的重置密码页面。
|
||||
*/
|
||||
public function edit(Request $request, string $token): View
|
||||
{
|
||||
return view('password-reset', [
|
||||
'systemName' => Sysparam::where('alias', 'sys_name')->value('body') ?? '和平聊吧',
|
||||
'token' => $token,
|
||||
'email' => (string) $request->query('email', ''),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交新的登录密码并完成重置。
|
||||
*/
|
||||
public function update(ResetPasswordRequest $request): RedirectResponse
|
||||
{
|
||||
$credentials = $request->validated();
|
||||
|
||||
$status = Password::reset(
|
||||
$credentials,
|
||||
function (User $user, #[\SensitiveParameter] string $password): void {
|
||||
// 重置成功后同步刷新 remember_token,避免旧设备继续沿用旧令牌。
|
||||
$user->forceFill([
|
||||
'password' => Hash::make($password),
|
||||
'remember_token' => Str::random(60),
|
||||
])->save();
|
||||
|
||||
event(new PasswordReset($user));
|
||||
}
|
||||
);
|
||||
|
||||
if ($status === Password::PASSWORD_RESET) {
|
||||
return redirect()->route('home')->with('status', '密码已重置成功,请使用原昵称和新密码重新登录。');
|
||||
}
|
||||
|
||||
return back()
|
||||
->withInput($request->except('password', 'password_confirmation'))
|
||||
->withErrors([
|
||||
'email' => $this->resolveResetFailureMessage($status),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断系统是否已开启邮箱发信服务。
|
||||
*/
|
||||
private function isPasswordResetMailEnabled(): bool
|
||||
{
|
||||
return Sysparam::where('alias', 'smtp_enabled')->value('body') === '1';
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Laravel 密码重置状态码转换为中文错误提示。
|
||||
*/
|
||||
private function resolveResetFailureMessage(string $status): string
|
||||
{
|
||||
return match ($status) {
|
||||
Password::INVALID_TOKEN => '重置链接无效或已过期,请重新申请邮箱找回。',
|
||||
Password::INVALID_USER => '该邮箱未绑定可重置的账号,请确认后再试。',
|
||||
default => '密码重置失败,请重新获取重置链接后再试。',
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:前台邮箱重置密码请求验证器
|
||||
*
|
||||
* 负责校验重置令牌、邮箱和新密码字段。
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
/**
|
||||
* 类功能:校验邮箱重置密码表单提交的数据。
|
||||
*/
|
||||
class ResetPasswordRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* 判断当前请求是否允许继续执行。
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义重置密码请求的验证规则。
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'token' => ['required', 'string'],
|
||||
'email' => ['required', 'email', 'max:255'],
|
||||
'password' => ['required', 'string', 'min:6', 'confirmed'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义重置密码请求的中文错误提示。
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'token.required' => '重置凭证缺失,请重新从邮件中的链接进入。',
|
||||
'email.required' => '邮箱不能为空。',
|
||||
'email.email' => '邮箱格式不正确。',
|
||||
'password.required' => '请输入新的登录密码。',
|
||||
'password.min' => '新密码长度至少需要 6 位。',
|
||||
'password.confirmed' => '两次输入的新密码不一致。',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:发送邮箱找回密码链接请求验证器
|
||||
*
|
||||
* 负责校验独立找回密码页面提交的邮箱字段。
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
/**
|
||||
* 类功能:校验邮箱找回密码所需的邮箱参数。
|
||||
*/
|
||||
class SendPasswordResetLinkRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* 判断当前请求是否允许继续执行。
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义邮箱找回密码请求的验证规则。
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'email' => ['required', 'email', 'max:255'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义邮箱找回密码请求的中文错误提示。
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'email.required' => '请输入已绑定账号的邮箱地址。',
|
||||
'email.email' => '邮箱格式不正确,请重新输入。',
|
||||
'email.max' => '邮箱长度不能超过 255 个字符。',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class UpdateProfileRequest extends FormRequest
|
||||
{
|
||||
@@ -33,7 +34,7 @@ class UpdateProfileRequest extends FormRequest
|
||||
'sex' => ['required', 'in:0,1,2'],
|
||||
'headface' => ['required', 'string', 'max:50'], // 比如存放 01.gif - 50.gif
|
||||
'sign' => ['nullable', 'string', 'max:255'],
|
||||
'email' => ['nullable', 'email', 'max:255'],
|
||||
'email' => ['nullable', 'email', 'max:255', Rule::unique('users', 'email')->ignore($this->user()?->id)],
|
||||
'question' => ['nullable', 'string', 'max:100'],
|
||||
'answer' => ['nullable', 'string', 'max:100'],
|
||||
];
|
||||
@@ -44,6 +45,7 @@ class UpdateProfileRequest extends FormRequest
|
||||
return [
|
||||
'sex.in' => '性别选项无效(0=保密 1=男 2=女)。',
|
||||
'headface.required' => '必须选择一个头像。',
|
||||
'email.unique' => '该邮箱已被其他账号绑定,请更换一个邮箱。',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user