React Native KeyChain完整封装

一、完整封装代码

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
  1. Info.plist 添加:
xml 复制代码
<key>NSFaceIDUsageDescription</key>
<string>App需要Face ID进行安全验证</string>
Android
  1. AndroidManifest.xml 添加:
xml 复制代码
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
  1. 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,频繁的生物识别验证会严重影响用户体验。

总结到此!

相关推荐
用户693717500138425 分钟前
Google 正在“收紧侧加载”:陌生 APK 安装或需等待 24 小时
android·前端
蓝帆傲亦29 分钟前
Web 前端搜索文字高亮实现方法汇总
前端
用户693717500138430 分钟前
Room 3.0:这次不是升级,是重来
android·前端·google
漫随流水2 小时前
旅游推荐系统(view.py)
前端·数据库·python·旅游
踩着两条虫3 小时前
VTJ.PRO 核心架构全公开!从设计稿到代码,揭秘AI智能体如何“听懂人话”
前端·vue.js·ai编程
jzlhll1234 小时前
kotlin Flow first() last()总结
开发语言·前端·kotlin
用头发抵命4 小时前
Vue 3 中优雅地集成 Video.js 播放器:从组件封装到功能定制
开发语言·javascript·ecmascript
蓝冰凌5 小时前
Vue 3 中 defineExpose 的行为【defineExpose暴露ref变量】详解:自动解包、响应性与实际使用
前端·javascript·vue.js
奔跑的呱呱牛5 小时前
generate-route-vue基于文件系统的 Vue Router 动态路由生成工具
前端·javascript·vue.js