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

React Native Expo + AWS Amplify实现离线登录功能方案咨询

基于React Native Expo + AWS Amplify Cognito实现离线认证方案

核心思路

AWS Amplify Cognito默认提供内存+本地缓存,但离线场景下需要更持久的凭证存储。结合你已使用的SQLite,核心逻辑是:联网时将Cognito返回的认证凭证(ID Token、Refresh Token、过期时间)存入SQLite;离线时读取本地存储的凭证,验证有效性后模拟Amplify的用户会话,实现离线登录;网络恢复时自动刷新凭证并更新本地存储。


一、前期依赖配置

确保项目已集成必要依赖:

expo install aws-amplify @aws-amplify/react-native expo-sqlite expo-network expo-secure-store
  • expo-secure-store用于加密存储敏感凭证(替代SQLite明文存token)
  • expo-network用于检测网络状态

初始化Amplify Auth并配置Cognito(按官方流程完成,确保开启本地缓存)。


二、SQLite数据库初始化

创建用于存储认证信息的表,结合SecureStore加密敏感字段:

import * as SQLite from 'expo-sqlite';
import * as SecureStore from 'expo-secure-store';

// 初始化SQLite数据库
const db = SQLite.openDatabase('auth.db');
const initAuthDB = () => {
  db.transaction(tx => {
    tx.executeSql(
      'CREATE TABLE IF NOT EXISTS user_auth (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE, expires_at INTEGER)'
    );
  });
};

三、联网登录实现

调用Amplify Auth.signIn成功后,将凭证存入SQLite+SecureStore:

import { Auth } from 'aws-amplify';

const signInOnline = async (username, password) => {
  try {
    // 联网登录Cognito
    await Auth.signIn(username, password);
    const session = await Auth.currentSession();
    const { idToken, refreshToken } = session;

    // 用SecureStore加密存储token
    await SecureStore.setItemAsync(`id_token_${username}`, idToken.jwtToken);
    await SecureStore.setItemAsync(`refresh_token_${username}`, refreshToken.token);

    // 非敏感信息存入SQLite
    db.transaction(tx => {
      tx.executeSql('REPLACE INTO user_auth (username, expires_at) VALUES (?, ?)', [
        username,
        idToken.payload.exp * 1000 // 转成时间戳
      ]);
    });

    return session;
  } catch (err) {
    console.error('在线登录失败:', err);
    throw err;
  }
};

四、联网登出实现

调用Amplify登出接口,同时清空本地存储的所有凭证:

const signOutOnline = async () => {
  try {
    await Auth.signOut();
    // 获取本地存储的用户名,清空对应凭证
    db.transaction(tx => {
      tx.executeSql('SELECT username FROM user_auth LIMIT 1', [], async (_, { rows }) => {
        if (rows.length > 0) {
          const username = rows.item(0).username;
          await SecureStore.deleteItemAsync(`id_token_${username}`);
          await SecureStore.deleteItemAsync(`refresh_token_${username}`);
        }
        tx.executeSql('DELETE FROM user_auth');
      });
    });
  } catch (err) {
    console.error('在线登出失败:', err);
    throw err;
  }
};

五、离线登录逻辑实现

检测网络状态,离线时读取本地凭证并验证有效性,模拟Amplify用户会话:

import * as Network from 'expo-network';
import { Cache } from 'aws-amplify';

const signInOffline = async () => {
  const networkState = await Network.getNetworkStateAsync();
  if (networkState.isConnected) throw new Error('当前处于联网状态,请使用在线登录');

  return new Promise((resolve, reject) => {
    db.transaction(tx => {
      tx.executeSql('SELECT * FROM user_auth LIMIT 1', [], async (_, { rows }) => {
        if (rows.length === 0) {
          reject(new Error('无本地登录凭证,请先联网登录'));
          return;
        }

        const { username, expires_at } = rows.item(0);
        const now = Date.now();

        // 验证凭证是否过期
        if (expires_at < now) {
          reject(new Error('本地凭证已过期,请联网重新登录'));
          return;
        }

        // 从SecureStore读取加密的token
        const idToken = await SecureStore.getItemAsync(`id_token_${username}`);
        const refreshToken = await SecureStore.getItemAsync(`refresh_token_${username}`);

        if (!idToken || !refreshToken) {
          reject(new Error('本地凭证损坏,请联网重新登录'));
          return;
        }

        // 构造Amplify兼容的用户会话,写入Amplify缓存
        const mockSession = {
          username,
          signInUserSession: {
            idToken: { jwtToken: idToken },
            refreshToken: { token: refreshToken }
          }
        };
        await Cache.setItem('currentUser', mockSession);

        resolve(mockSession);
      });
    });
  });
};

六、网络恢复后的自动同步

监听网络状态变化,联网后自动刷新凭证并更新本地存储:

const setupNetworkSync = () => {
  Network.addNetworkStateListener(async state => {
    if (state.isConnected) await refreshAuthToken();
  });
};

const refreshAuthToken = async () => {
  try {
    const session = await Auth.currentSession();
    const newSession = await Auth.refreshSession(session.refreshToken);
    const { idToken, refreshToken } = newSession;
    const username = session.idToken.payload.username;

    // 更新本地存储
    await SecureStore.setItemAsync(`id_token_${username}`, idToken.jwtToken);
    await SecureStore.setItemAsync(`refresh_token_${username}`, refreshToken.token);
    db.transaction(tx => {
      tx.executeSql('UPDATE user_auth SET expires_at = ? WHERE username = ?', [
        idToken.payload.exp * 1000,
        username
      ]);
    });
  } catch (err) {
    console.error('凭证刷新失败:', err);
    // 刷新失败需引导用户重新登录
  }
};

关键注意事项

  1. 凭证加密:必须用expo-secure-store加密存储token,避免SQLite明文泄露敏感信息。
  2. 过期验证:离线登录时必须检查token过期时间,避免使用失效凭证。
  3. Amplify缓存兼容:模拟的用户会话要严格匹配Amplify的结构,确保后续Auth.currentUserInfo()等方法能正常读取。
  4. 数据同步:如果应用有业务数据,建议搭配Amplify DataStore实现离线数据同步,与认证逻辑联动。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 19:05:04