fix(auth): 修复找回密码流程的三个 bug
Backend: - password.service.ts: resetPassword 补齐验证码暴力破解防护, 输错时累计 attempts,超过 5 次拒绝,与 loginBySms 逻辑一致 Frontend (forgot_password_page.dart): - 发送验证码错误消息改为提取真实 message,不再显示原始异常类名 - 重置密码错误消息同上处理 - 新增 _sendingSms 标志,发送请求期间禁用按钮,防止重复发短信 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
761dcb1115
commit
149cf7ea77
|
|
@ -45,8 +45,15 @@ export class PasswordService {
|
|||
SmsVerificationType.RESET_PASSWORD,
|
||||
);
|
||||
|
||||
if (!verification || verification.code !== dto.smsCode) {
|
||||
throw new BadRequestException('验证码错误或已过期');
|
||||
if (!verification) {
|
||||
throw new BadRequestException('验证码已过期或不存在');
|
||||
}
|
||||
if (verification.attempts >= 5) {
|
||||
throw new BadRequestException('验证码尝试次数过多,请重新获取');
|
||||
}
|
||||
if (verification.code !== dto.smsCode) {
|
||||
await this.smsVerificationRepository.incrementAttempts(verification.id);
|
||||
throw new BadRequestException('验证码错误');
|
||||
}
|
||||
|
||||
// 标记验证码已使用
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:go_router/go_router.dart';
|
||||
import '../../../core/router/routes.dart';
|
||||
import '../../../core/constants/app_colors.dart';
|
||||
import '../../../core/error/exceptions.dart';
|
||||
import '../../providers/user_providers.dart';
|
||||
|
||||
class ForgotPasswordPage extends ConsumerStatefulWidget {
|
||||
|
|
@ -22,6 +23,7 @@ class _ForgotPasswordPageState extends ConsumerState<ForgotPasswordPage> {
|
|||
bool _obscurePassword = true;
|
||||
bool _obscureConfirmPassword = true;
|
||||
int _countDown = 0;
|
||||
bool _sendingSms = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
|
|
@ -41,21 +43,27 @@ class _ForgotPasswordPageState extends ConsumerState<ForgotPasswordPage> {
|
|||
return;
|
||||
}
|
||||
|
||||
setState(() => _sendingSms = true);
|
||||
try {
|
||||
await ref.read(userNotifierProvider.notifier).sendSmsCode(phone, 'RESET_PASSWORD');
|
||||
setState(() => _countDown = 60);
|
||||
_startCountDown();
|
||||
if (mounted) {
|
||||
setState(() => _countDown = 60);
|
||||
_startCountDown();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('验证码已发送')),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
final msg = e is ServerException || e is NetworkException
|
||||
? (e as dynamic).message as String
|
||||
: e.toString().replaceFirst(RegExp(r'^[A-Za-z]+Exception:\s*'), '');
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('发送失败: $e')),
|
||||
SnackBar(content: Text(msg)),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) setState(() => _sendingSms = false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -86,8 +94,11 @@ class _ForgotPasswordPageState extends ConsumerState<ForgotPasswordPage> {
|
|||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
final msg = e is ServerException || e is NetworkException
|
||||
? (e as dynamic).message as String
|
||||
: e.toString().replaceFirst(RegExp(r'^[A-Za-z]+Exception:\s*'), '');
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('重置失败: $e')),
|
||||
SnackBar(content: Text(msg)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -191,7 +202,7 @@ class _ForgotPasswordPageState extends ConsumerState<ForgotPasswordPage> {
|
|||
width: 120,
|
||||
height: 56,
|
||||
child: ElevatedButton(
|
||||
onPressed: _countDown > 0 ? null : _sendSmsCode,
|
||||
onPressed: (_countDown > 0 || _sendingSms) ? null : _sendSmsCode,
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
|
|
|
|||
Loading…
Reference in New Issue