Flutter中如何安全存储与检索自定义图案锁屏?
Flutter 安全存储与检索图案锁方案
核心原则:绝不存储明文
Shared Preferences 默认是明文存储,直接存图案会有被恶意读取的风险,所以必须先加密再存储,优先使用系统级安全存储方案。
方案一:使用 flutter_secure_storage(推荐)
这个插件会利用系统自带的安全存储(Android Keystore、iOS Keychain),比自行加密更可靠。
步骤:
- 添加依赖
在pubspec.yaml中加入:
dependencies: flutter_secure_storage: ^9.0.0 # 替换为最新版本
- 初始化存储实例
final secureStorage = FlutterSecureStorage();
- 存储图案
把图案序列转成字符串后存储:
// 假设pattern是用户设置的图案,比如List<int> pattern = [0,1,2,4] Future<void> savePatternLock(List<int> pattern) async { String patternStr = pattern.join(','); await secureStorage.write(key: 'app_pattern_lock', value: patternStr); }
- 检索图案
读取后转回序列:
Future<List<int>?> getPatternLock() async { String? storedPatternStr = await secureStorage.read(key: 'app_pattern_lock'); if (storedPatternStr != null) { return storedPatternStr.split(',').map(int.parse).toList(); } return null; }
方案二:加密后存入 Shared Preferences
如果坚持用 Shared Preferences,必须配合加密插件(比如 encrypt)处理。
步骤:
- 添加依赖
dependencies: shared_preferences: ^2.2.2 # 最新版本 encrypt: ^5.0.1 # 最新版本 crypto: ^3.0.3 # 用于恒定时间比较,避免时序攻击
- 加密存储逻辑
import 'package:encrypt/encrypt.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:crypto/crypto.dart'; // 建议将密钥存在flutter_secure_storage中,避免硬编码 final key = Key.fromSecureRandom(32); final iv = IV.fromSecureRandom(16); final encrypter = Encrypter(AES(key)); // 存储加密后的图案 Future<void> saveEncryptedPattern(List<int> pattern) async { final prefs = await SharedPreferences.getInstance(); String patternStr = pattern.join(','); String encrypted = encrypter.encrypt(patternStr, iv: iv).base64; await prefs.setString('encrypted_pattern', encrypted); await prefs.setString('pattern_iv', iv.base64); // IV可明文存储 } // 解密并获取图案 Future<List<int>?> getEncryptedPattern() async { final prefs = await SharedPreferences.getInstance(); String? encrypted = prefs.getString('encrypted_pattern'); String? ivStr = prefs.getString('pattern_iv'); if (encrypted != null && ivStr != null) { final iv = IV.fromBase64(ivStr); String decrypted = encrypter.decrypt(Encrypted.fromBase64(encrypted), iv: iv); return decrypted.split(',').map(int.parse).toList(); } return null; }
- 安全对比图案
避免时序攻击,用恒定时间比较:
bool isPatternMatch(List<int> input, List<int> stored) { if (input.length != stored.length) return false; var inputBytes = utf8.encode(input.join(',')); var storedBytes = utf8.encode(stored.join(',')); return constantTimeEquals(inputBytes, storedBytes); }
应用启动时的校验逻辑
在 main 函数中检查是否有已存储的图案,决定跳转页面:
void main() async { WidgetsFlutterBinding.ensureInitialized(); List<int>? storedPattern = await getPatternLock(); // 或getEncryptedPattern() runApp(MaterialApp( home: storedPattern != null ? PatternLockVerifyScreen() : HomeScreen(), )); }
内容的提问来源于stack exchange,提问作者Saurabh Kumar
相关产品推荐
相关产品推荐

