You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter集成Firebase时如何全局限制密码重置请求次数

全局保存密码重置请求次数的解决方案

你之前在视图层维护的callTimes是局部变量,页面销毁重建后就会重置,要实现全局持久化的次数限制,有以下几种可靠方案:

1. 本地持久化(SharedPreferences)

这种方案把请求时间戳存在设备本地,跨页面切换甚至应用重启都能保留,适合单设备的次数限制。

步骤:

  1. 添加依赖:在pubspec.yaml中加入shared_preferences包
  2. 封装工具类统一管理存储逻辑
  3. 修改密码重置方法,调用工具类判断是否可发起请求

工具类代码:

import 'package:shared_preferences/shared_preferences.dart';

class ResetRequestLimiter {
  static const String _storageKey = 'password_reset_timestamps';
  static const int _maxRequests = 10;
  static const int _timeWindowMs = 600000; // 10分钟

  // 读取本地存储的时间戳列表
  static Future<List<int>> _getTimestamps() async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getStringList(_storageKey)?.map(int.parse).toList() ?? [];
  }

  // 保存时间戳列表到本地
  static Future<void> _saveTimestamps(List<int> timestamps) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setStringList(_storageKey, timestamps.map((t) => t.toString()).toList());
  }

  // 判断是否允许发起重置请求
  static Future<bool> canRequestReset() async {
    final now = DateTime.now().millisecondsSinceEpoch;
    var timestamps = await _getTimestamps();
    
    // 清除超过10分钟的旧记录
    timestamps.removeWhere((timestamp) => now - timestamp > _timeWindowMs);
    
    if (timestamps.length < _maxRequests) {
      timestamps.add(now);
      await _saveTimestamps(timestamps);
      return true;
    }
    return false;
  }
}

修改后的密码重置方法:

Future<void> passwordReset(BuildContext context) async {
  try {
    final canReset = await ResetRequestLimiter.canRequestReset();
    
    if (canReset) {
      await FirebaseAuth.instance
          .sendPasswordResetEmail(email: _emailController.text.trim());
      showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            content: Text('如果该邮箱在系统中存在,密码重置邮件已发送。'),
          );
        },
      );
      Navigator.of(context).pop();
    } else {
      showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            content: Text('密码重置请求次数超限,请稍后再试。'),
          );
        },
      );
    }
  } catch (e) {
    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          content: Text(e.toString()),
        );
      },
    );
  }
}

2. 全局状态管理(以Provider为例)

如果不需要跨应用重启保留记录,只是希望在应用运行期间全局共享请求次数,可以用状态管理方案,比如Provider、GetX等。

步骤:

  1. 添加provider依赖到pubspec.yaml
  2. 创建状态类维护请求时间戳
  3. 在应用入口注册状态
  4. 在页面中获取状态并判断

状态类代码:

import 'package:flutter/foundation.dart';

class ResetRequestState extends ChangeNotifier {
  final List<int> _callTimes = [];
  static const int _maxRequests = 10;
  static const int _timeWindowMs = 600000;

  bool canRequestReset() {
    final now = DateTime.now().millisecondsSinceEpoch;
    // 清理过期记录
    _callTimes.removeWhere((timestamp) => now - timestamp > _timeWindowMs);
    
    if (_callTimes.length < _maxRequests) {
      _callTimes.add(now);
      notifyListeners();
      return true;
    }
    return false;
  }
}

应用入口注册:

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => ResetRequestState(),
      child: const MyApp(),
    ),
  );
}

页面中使用:

Future<void> passwordReset(BuildContext context) async {
  try {
    final resetState = Provider.of<ResetRequestState>(context, listen: false);
    final canReset = resetState.canRequestReset();
    
    if (canReset) {
      await FirebaseAuth.instance
          .sendPasswordResetEmail(email: _emailController.text.trim());
      showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            content: Text('如果该邮箱在系统中存在,密码重置邮件已发送。'),
          );
        },
      );
      Navigator.of(context).pop();
    } else {
      showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            content: Text('密码重置请求次数超限,请稍后再试。'),
          );
        },
      );
    }
  } catch (e) {
    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          content: Text(e.toString()),
        );
      },
    );
  }
}

3. 后端存储(Firebase Firestore)

如果需要跨设备限制同一个邮箱的请求次数,或者防止用户通过清除应用数据绕过限制,就需要把请求记录存在Firebase后端。

步骤:

  1. 确保已集成Firebase Firestore
  2. 创建集合存储请求记录
  3. 每次请求前查询该邮箱最近10分钟的请求次数
  4. 根据查询结果判断是否允许发起请求

核心逻辑代码:

import 'package:cloud_firestore/cloud_firestore.dart';

// 判断指定邮箱是否可发起重置请求
Future<bool> canRequestReset(String email) async {
  final now = DateTime.now();
  final tenMinutesAgo = now.subtract(const Duration(minutes: 10));
  
  // 查询最近10分钟内的请求记录
  final querySnapshot = await FirebaseFirestore.instance
      .collection('password_reset_requests')
      .where('email', isEqualTo: email)
      .where('timestamp', isGreaterThan: tenMinutesAgo)
      .get();
  
  if (querySnapshot.docs.length < 10) {
    // 记录本次请求
    await FirebaseFirestore.instance
        .collection('password_reset_requests')
        .add({
          'email': email,
          'timestamp': FieldValue.serverTimestamp(),
        });
    return true;
  }
  return false;
}

修改后的密码重置方法:

Future<void> passwordReset(BuildContext context) async {
  try {
    final email = _emailController.text.trim();
    final canReset = await canRequestReset(email);
    
    if (canReset) {
      await FirebaseAuth.instance.sendPasswordResetEmail(email: email);
      showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            content: Text('如果该邮箱在系统中存在,密码重置邮件已发送。'),
          );
        },
      );
      Navigator.of(context).pop();
    } else {
      showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            content: Text('密码重置请求次数超限,请稍后再试。'),
          );
        },
      );
    }
  } catch (e) {
    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          content: Text(e.toString()),
        );
      },
    );
  }
}

方案选择建议

  • 单设备限制、无需跨重启:选本地持久化
  • 应用运行期间全局共享、无需跨重启:选全局状态管理
  • 跨设备限制、防篡改:选后端存储

内容的提问来源于stack exchange,提问作者Tevel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 12:47:12