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,频繁的生物识别验证会严重影响用户体验。

总结到此!

相关推荐
回眸&啤酒鸭43 分钟前
【回眸】OpenSwarm 多智能体协作系统实战指南
大数据·前端·人工智能
用户69371750013841 小时前
2026,程序员的时代拐点到了
android·前端·后端
大龄秃头程序员1 小时前
一次 iBeacon + BLE 无感解锁方案的实现记录
前端
小兔子1 小时前
Python 的 GIL 与 free-threading:3.13 之后「去 GIL」走到哪一步了
前端
IT_陈寒1 小时前
SpringBoot自动配置差点让我加班到凌晨
前端·人工智能·后端
guslegend2 小时前
脚手架原理与本地调试:从 bin 软链接到 npm link
前端·npm·node.js·脚手架·前端工程化
小宋10212 小时前
A2UI从零实战:Agent安全生成交互界面与事件回传
javascript·人工智能·安全·交互
大包子3 小时前
structuredClone vs lodash.cloneDeep:核心差异与选型指南
javascript·vue.js
海码事务所3 小时前
Google Play 新个人开发者账号上架指南:12 人连续 14 天封闭测试怎么做?
前端
田威AI3 小时前
图片内文字翻译的规格:输入输出、保真、自动化、时间与费用
前端·计算机视觉