一、完整封装代码
javascript
/**
* @author Dragon Wu
* @created 2026/01/20 14:04
* @description KeyChain的封装
*/
import APP_ENV from '@/config/env';
import * as Keychain from 'react-native-keychain';
// === 安全存储函数(Keychain基础加密)===
export const saveSecure = async (
key: string,
value: unknown,
options?: Keychain.SetOptions
): Promise<boolean> => {
const data = typeof value === 'string' ? value : JSON.stringify(value);
const result = await Keychain.setGenericPassword(key, data, {
service: APP_ENV.keychainService,
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED,
...(options ?? {})
});
return !!result;
};
// 加载普通安全存储的数据
export const loadSecure = async <T = unknown>(key: string): Promise<T | null> => {
const creds = await Keychain.getGenericPassword({ service: APP_ENV.keychainService });
if (!creds || creds.username !== key) return null;
try {
return JSON.parse(creds.password);
} catch {
return creds.password as T;
}
};
export const removeSecure = async (key: string): Promise<boolean> => {
return await Keychain.resetGenericPassword({ service: key });
};
// === 生物识别存储函数(最高安全级别)===
export const saveWithBiometry = async (
key: string,
value: unknown,
options?: Keychain.SetOptions
): Promise<boolean> => {
const data = typeof value === 'string' ? value : JSON.stringify(value);
const result = await Keychain.setGenericPassword(key, data, {
service: APP_ENV.keychainService,
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED,
accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY,
...(options ?? {})
});
return result !== false;
};
// 加载生物识别存储的数据
export const loadWithBiometry = async <T = unknown>(
key: string,
promptOptions?: Keychain.AuthenticationPrompt
): Promise<T | null> => {
const creds = await Keychain.getGenericPassword({
service: APP_ENV.keychainService,
authenticationPrompt: {
title: '验证身份',
cancel: '取消',
...(promptOptions ?? {})
}
});
if (!creds || creds.username !== key) return null;
try {
return JSON.parse(creds.password);
} catch {
return creds.password as T;
}
};
// 判断生物识别是否可用
export const isBiometryAvailable = async (): Promise<boolean> => {
const type = await Keychain.getSupportedBiometryType();
return type !== null;
};
// 清理所有keychain存储
export const clearAllCredentials = async (): Promise<void> => {
await Keychain.resetGenericPassword();
};
Keychain 本地加密存储
为什么需要 accessible 配置?
src\utils\storage\secureStorage.ts里默认配置了WHEN_UNLOCKED
accessible 选项 |
安全级别 | 适用场景 |
|---|---|---|
WHEN_UNLOCKED_THIS_DEVICE_ONLY |
🔒🔒🔒🔒 最高 | 设备解锁后访问,不跨设备同步 |
WHEN_UNLOCKED |
🔒🔒🔒 高 | 设备解锁后访问,可跨设备同步 |
AFTER_FIRST_UNLOCK |
🔒🔒 中 | 首次解锁后即可访问 |
ALWAYS |
🔒 低 | 随时可访问,包括设备锁定时 |
| Token 类型 | 推荐配置 | 原因 |
|---|---|---|
access_token |
WHEN_UNLOCKED |
平衡安全与体验,支持跨设备同步 |
refresh_token |
AFTER_FIRST_UNLOCK |
允许后台刷新,避免频繁登录 |
生物识别安全存储
若需要使用生物识别安全存储功能,需开启以下权限配置
需要配置权限声明和最低系统版本。
📱 具体配置
iOS
- Info.plist 添加:
xml
<key>NSFaceIDUsageDescription</key>
<string>App需要Face ID进行安全验证</string>
Android
- AndroidManifest.xml 添加:
xml
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
- build.gradle 确保:
gradle
minSdkVersion 23 // Android 6.0+
⚠️ 重要提醒
- iOS:仅Face ID需要配置,Touch ID不需要
- Android:从API 23(Android 6.0)开始支持
- 模拟器:只能测试流程,无法真实验证
配置完这些,react-native-keychain的生物识别功能才能正常工作。
什么时候需要生物识别
access_token 直接用 Keychain 存储就够了,不需要生物识别。
📊 核心原因
| 场景 | 生物识别存储 | Keychain普通存储 | 建议 |
|---|---|---|---|
| 每次请求API | ❌ 需反复验证指纹/面容 | ✅ 自动读取 | Keychain |
| 短期有效性 | ⚠️ 过度保护(2小时过期) | ✅ 风险可控 | Keychain |
| 实际安全需求 | 适合支付密码等 | ✅ 已有设备级加密 | Keychain |
🎯 具体方案
typescript
// ✅ 正确做法:普通Keychain存储
import { saveSecure } from './secureStorage';
// access_token - Keychain普通存储
await saveSecure('access_token', 'eyJhbGciOiJ...');
// refresh_token - 也可Keychain存储
await saveSecure('refresh_token', 'refresh_xyz');
生物识别留给真正的敏感数据:
- 银行卡PIN码
- 生物特征密钥
- 医疗健康数据
Keychain的加密已经足够保护 token,频繁的生物识别验证会严重影响用户体验。
总结到此!