HarmonyOS应用<节气通>开发第30篇:安全存储封装

引言

安全存储是应用开发中保护敏感数据的重要手段。本文将介绍如何封装一个安全存储系统,包括:

  • 安全存储API封装
  • 数据加密存储
  • Token管理
  • 敏感数据保护

通过本文,你将掌握如何构建安全的存储系统。


学习目标

完成本文后,你将能够:

  • ✅ 封装安全存储API
  • ✅ 实现数据加密
  • ✅ 管理Token
  • ✅ 保护敏感数据

需求分析

功能模块设计

模块 功能描述 技术要点
安全存储 封装安全存储API 数据加密、解密
Token管理 管理用户Token Token存储、获取、清除
数据加密 加密敏感数据 AES加密、Base64编码
密钥管理 管理加密密钥 密钥生成、存储

核心实现

步骤1: 安全存储API封装

typescript 复制代码
// storage/SecureStorage.ets

import { logger } from '../utils/Logger';

/**
 * 安全存储服务
 */
class SecureStorage {
  private static instance: SecureStorage;
  
  // 存储前缀
  private prefix: string = 'app_';
  
  /**
   * 获取单例实例
   */
  static getInstance(): SecureStorage {
    if (!SecureStorage.instance) {
      SecureStorage.instance = new SecureStorage();
    }
    return SecureStorage.instance;
  }
  
  /**
   * 设置存储前缀
   */
  setPrefix(prefix: string): void {
    this.prefix = prefix;
  }
  
  /**
   * 获取完整的存储键名
   */
  private getKey(key: string): string {
    return `${this.prefix}${key}`;
  }
  
  /**
   * 存储字符串
   */
  async setString(key: string, value: string): Promise<void> {
    try {
      await storage.set(this.getKey(key), value);
      logger.debug(`存储字符串: ${key}`);
    } catch (error) {
      logger.error(`存储字符串失败: ${key}`, error);
      throw error;
    }
  }
  
  /**
   * 获取字符串
   */
  async getString(key: string, defaultValue: string = ''): Promise<string> {
    try {
      const value = await storage.get(this.getKey(key), defaultValue);
      logger.debug(`获取字符串: ${key}`);
      return value;
    } catch (error) {
      logger.error(`获取字符串失败: ${key}`, error);
      return defaultValue;
    }
  }
  
  /**
   * 存储数字
   */
  async setNumber(key: string, value: number): Promise<void> {
    await this.setString(key, value.toString());
  }
  
  /**
   * 获取数字
   */
  async getNumber(key: string, defaultValue: number = 0): Promise<number> {
    const value = await this.getString(key, defaultValue.toString());
    return parseFloat(value);
  }
  
  /**
   * 存储布尔值
   */
  async setBoolean(key: string, value: boolean): Promise<void> {
    await this.setString(key, value ? 'true' : 'false');
  }
  
  /**
   * 获取布尔值
   */
  async getBoolean(key: string, defaultValue: boolean = false): Promise<boolean> {
    const value = await this.getString(key, defaultValue ? 'true' : 'false');
    return value === 'true';
  }
  
  /**
   * 存储对象
   */
  async setObject<T>(key: string, value: T): Promise<void> {
    try {
      const jsonString = JSON.stringify(value);
      await this.setString(key, jsonString);
      logger.debug(`存储对象: ${key}`);
    } catch (error) {
      logger.error(`存储对象失败: ${key}`, error);
      throw error;
    }
  }
  
  /**
   * 获取对象
   */
  async getObject<T>(key: string, defaultValue: T | null = null): Promise<T | null> {
    try {
      const jsonString = await this.getString(key);
      if (!jsonString) {
        return defaultValue;
      }
      const value = JSON.parse(jsonString) as T;
      logger.debug(`获取对象: ${key}`);
      return value;
    } catch (error) {
      logger.error(`获取对象失败: ${key}`, error);
      return defaultValue;
    }
  }
  
  /**
   * 删除存储项
   */
  async remove(key: string): Promise<void> {
    try {
      await storage.delete(this.getKey(key));
      logger.debug(`删除存储项: ${key}`);
    } catch (error) {
      logger.error(`删除存储项失败: ${key}`, error);
      throw error;
    }
  }
  
  /**
   * 清除所有存储
   */
  async clear(): Promise<void> {
    try {
      await storage.clear();
      logger.debug('清除所有存储');
    } catch (error) {
      logger.error('清除存储失败', error);
      throw error;
    }
  }
  
  /**
   * 检查存储项是否存在
   */
  async contains(key: string): Promise<boolean> {
    try {
      const value = await this.getString(key);
      return value !== '';
    } catch {
      return false;
    }
  }
}

// Mock storage API
const storage = {
  async set(key: string, value: string): Promise<void> {},
  async get(key: string, defaultValue: string): Promise<string> {
    return defaultValue;
  },
  async delete(key: string): Promise<void> {},
  async clear(): Promise<void> {}
};

/**
 * 全局安全存储实例
 */
export const secureStorage = SecureStorage.getInstance();

/**
 * 便捷存储方法
 */
export const Storage = {
  set: secureStorage.setString.bind(secureStorage),
  get: secureStorage.getString.bind(secureStorage),
  setObject: secureStorage.setObject.bind(secureStorage),
  getObject: secureStorage.getObject.bind(secureStorage),
  remove: secureStorage.remove.bind(secureStorage),
  clear: secureStorage.clear.bind(secureStorage)
};

设计要点:

  • 统一存储接口
  • 支持多种数据类型
  • 错误处理和日志记录

步骤2: 加密存储服务

typescript 复制代码
// storage/EncryptedStorage.ets

import { logger } from '../utils/Logger';

/**
 * 加密存储服务
 */
class EncryptedStorage {
  private static instance: EncryptedStorage;
  
  // 加密密钥(应该从安全渠道获取)
  private encryptionKey: string = 'default_encryption_key_12345';
  
  /**
   * 获取单例实例
   */
  static getInstance(): EncryptedStorage {
    if (!EncryptedStorage.instance) {
      EncryptedStorage.instance = new EncryptedStorage();
    }
    return EncryptedStorage.instance;
  }
  
  /**
   * 设置加密密钥
   */
  setEncryptionKey(key: string): void {
    this.encryptionKey = key;
  }
  
  /**
   * 加密数据
   */
  private encrypt(data: string): string {
    try {
      // 简单的加密实现(实际应用中应使用更安全的加密算法)
      let encrypted = '';
      for (let i = 0; i < data.length; i++) {
        const charCode = data.charCodeAt(i);
        const keyCode = this.encryptionKey.charCodeAt(i % this.encryptionKey.length);
        encrypted += String.fromCharCode(charCode ^ keyCode);
      }
      // Base64编码
      return this.base64Encode(encrypted);
    } catch (error) {
      logger.error('加密失败', error);
      throw error;
    }
  }
  
  /**
   * 解密数据
   */
  private decrypt(data: string): string {
    try {
      // Base64解码
      const decoded = this.base64Decode(data);
      
      // 解密
      let decrypted = '';
      for (let i = 0; i < decoded.length; i++) {
        const charCode = decoded.charCodeAt(i);
        const keyCode = this.encryptionKey.charCodeAt(i % this.encryptionKey.length);
        decrypted += String.fromCharCode(charCode ^ keyCode);
      }
      return decrypted;
    } catch (error) {
      logger.error('解密失败', error);
      throw error;
    }
  }
  
  /**
   * Base64编码
   */
  private base64Encode(str: string): string {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
    let result = '';
    let i = 0;
    
    while (i < str.length) {
      const char1 = str.charCodeAt(i++) || 0;
      const char2 = str.charCodeAt(i++) || 0;
      const char3 = str.charCodeAt(i++) || 0;
      
      const enc1 = char1 >> 2;
      const enc2 = ((char1 & 3) << 4) | (char2 >> 4);
      const enc3 = ((char2 & 15) << 2) | (char3 >> 6);
      const enc4 = char3 & 63;
      
      if (isNaN(char2)) enc3 = enc4 = 64;
      else if (isNaN(char3)) enc4 = 64;
      
      result += chars[enc1] + chars[enc2] + chars[enc3] + chars[enc4];
    }
    
    return result;
  }
  
  /**
   * Base64解码
   */
  private base64Decode(str: string): string {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
    let result = '';
    let i = 0;
    
    str = str.replace(/[^A-Za-z0-9+/=]/g, '');
    
    while (i < str.length) {
      const enc1 = chars.indexOf(str.charAt(i++));
      const enc2 = chars.indexOf(str.charAt(i++));
      const enc3 = chars.indexOf(str.charAt(i++));
      const enc4 = chars.indexOf(str.charAt(i++));
      
      const char1 = (enc1 << 2) | (enc2 >> 4);
      const char2 = ((enc2 & 15) << 4) | (enc3 >> 2);
      const char3 = ((enc3 & 3) << 6) | enc4;
      
      result += String.fromCharCode(char1);
      
      if (enc3 !== 64) result += String.fromCharCode(char2);
      if (enc4 !== 64) result += String.fromCharCode(char3);
    }
    
    return result;
  }
  
  /**
   * 存储加密数据
   */
  async setEncrypted(key: string, value: string): Promise<void> {
    try {
      const encrypted = this.encrypt(value);
      await storage.set(key, encrypted);
      logger.debug(`存储加密数据: ${key}`);
    } catch (error) {
      logger.error(`存储加密数据失败: ${key}`, error);
      throw error;
    }
  }
  
  /**
   * 获取加密数据
   */
  async getEncrypted(key: string, defaultValue: string = ''): Promise<string> {
    try {
      const encrypted = await storage.get(key, '');
      if (!encrypted) {
        return defaultValue;
      }
      const decrypted = this.decrypt(encrypted);
      logger.debug(`获取加密数据: ${key}`);
      return decrypted;
    } catch (error) {
      logger.error(`获取加密数据失败: ${key}`, error);
      return defaultValue;
    }
  }
  
  /**
   * 存储加密对象
   */
  async setEncryptedObject<T>(key: string, value: T): Promise<void> {
    const jsonString = JSON.stringify(value);
    await this.setEncrypted(key, jsonString);
  }
  
  /**
   * 获取加密对象
   */
  async getEncryptedObject<T>(key: string, defaultValue: T | null = null): Promise<T | null> {
    const jsonString = await this.getEncrypted(key);
    if (!jsonString) {
      return defaultValue;
    }
    try {
      return JSON.parse(jsonString) as T;
    } catch {
      return defaultValue;
    }
  }
}

// Mock storage API
const storage = {
  async set(key: string, value: string): Promise<void> {},
  async get(key: string, defaultValue: string): Promise<string> {
    return defaultValue;
  }
};

/**
 * 全局加密存储实例
 */
export const encryptedStorage = EncryptedStorage.getInstance();

设计要点:

  • XOR加密算法
  • Base64编码
  • 加密对象存储

步骤3: Token管理器

typescript 复制代码
// storage/TokenManager.ets

import { logger } from '../utils/Logger';
import { secureStorage, encryptedStorage } from './SecureStorage';

/**
 * Token管理器
 */
class TokenManager {
  private static instance: TokenManager;
  
  // Token存储键名
  private static readonly ACCESS_TOKEN_KEY = 'access_token';
  private static readonly REFRESH_TOKEN_KEY = 'refresh_token';
  private static readonly TOKEN_EXPIRE_KEY = 'token_expire';
  private static readonly USER_INFO_KEY = 'user_info';
  
  /**
   * 获取单例实例
   */
  static getInstance(): TokenManager {
    if (!TokenManager.instance) {
      TokenManager.instance = new TokenManager();
    }
    return TokenManager.instance;
  }
  
  /**
   * 设置Token
   */
  async setToken(accessToken: string, refreshToken: string, expireTime: number): Promise<void> {
    try {
      // 使用加密存储保存Token
      await encryptedStorage.setEncrypted(TokenManager.ACCESS_TOKEN_KEY, accessToken);
      await encryptedStorage.setEncrypted(TokenManager.REFRESH_TOKEN_KEY, refreshToken);
      await secureStorage.setNumber(TokenManager.TOKEN_EXPIRE_KEY, expireTime);
      
      logger.info('Token设置成功');
    } catch (error) {
      logger.error('Token设置失败', error);
      throw error;
    }
  }
  
  /**
   * 获取Access Token
   */
  async getAccessToken(): Promise<string | null> {
    try {
      // 检查Token是否过期
      if (await this.isTokenExpired()) {
        // 尝试刷新Token
        const refreshed = await this.refreshToken();
        if (refreshed) {
          return await encryptedStorage.getEncrypted(TokenManager.ACCESS_TOKEN_KEY);
        }
        return null;
      }
      
      return await encryptedStorage.getEncrypted(TokenManager.ACCESS_TOKEN_KEY);
    } catch (error) {
      logger.error('获取Access Token失败', error);
      return null;
    }
  }
  
  /**
   * 获取Refresh Token
   */
  async getRefreshToken(): Promise<string | null> {
    try {
      return await encryptedStorage.getEncrypted(TokenManager.REFRESH_TOKEN_KEY);
    } catch (error) {
      logger.error('获取Refresh Token失败', error);
      return null;
    }
  }
  
  /**
   * 检查Token是否过期
   */
  async isTokenExpired(): Promise<boolean> {
    try {
      const expireTime = await secureStorage.getNumber(TokenManager.TOKEN_EXPIRE_KEY);
      const now = Date.now();
      return expireTime > 0 && now >= expireTime;
    } catch {
      return true;
    }
  }
  
  /**
   * 刷新Token
   */
  async refreshToken(): Promise<boolean> {
    try {
      const refreshToken = await this.getRefreshToken();
      if (!refreshToken) {
        return false;
      }
      
      // 调用刷新Token API
      // const response = await httpService.post('/api/refresh', { refreshToken });
      // if (response.success) {
      //   await this.setToken(response.data.accessToken, response.data.refreshToken, response.data.expireTime);
      //   return true;
      // }
      
      // 模拟刷新失败
      logger.warn('Token刷新失败,需要重新登录');
      await this.clearToken();
      return false;
    } catch (error) {
      logger.error('Token刷新失败', error);
      await this.clearToken();
      return false;
    }
  }
  
  /**
   * 清除Token
   */
  async clearToken(): Promise<void> {
    try {
      await secureStorage.remove(TokenManager.ACCESS_TOKEN_KEY);
      await secureStorage.remove(TokenManager.REFRESH_TOKEN_KEY);
      await secureStorage.remove(TokenManager.TOKEN_EXPIRE_KEY);
      await secureStorage.remove(TokenManager.USER_INFO_KEY);
      
      logger.info('Token已清除');
    } catch (error) {
      logger.error('清除Token失败', error);
      throw error;
    }
  }
  
  /**
   * 检查是否已登录
   */
  async isLoggedIn(): Promise<boolean> {
    const token = await this.getAccessToken();
    return !!token;
  }
  
  /**
   * 设置用户信息
   */
  async setUserInfo(userInfo: UserInfo): Promise<void> {
    await secureStorage.setObject(TokenManager.USER_INFO_KEY, userInfo);
  }
  
  /**
   * 获取用户信息
   */
  async getUserInfo(): Promise<UserInfo | null> {
    return await secureStorage.getObject<UserInfo>(TokenManager.USER_INFO_KEY);
  }
}

interface UserInfo {
  id: string;
  name: string;
  avatar: string;
  email: string;
}

/**
 * 全局Token管理器实例
 */
export const tokenManager = TokenManager.getInstance();

设计要点:

  • Token存储和管理
  • Token过期检查
  • Token刷新机制

步骤4: 在应用中使用安全存储

typescript 复制代码
// 在应用中使用安全存储

import { secureStorage, encryptedStorage } from './storage/SecureStorage';
import { tokenManager } from './storage/TokenManager';

@Entry
@Component
struct LoginPage {
  @State username: string = '';
  @State password: string = '';
  
  async onLogin() {
    try {
      // 调用登录API
      // const response = await httpService.post('/api/login', {
      //   username: this.username,
      //   password: this.password
      // });
      
      // 模拟登录成功
      const mockResponse = {
        accessToken: 'mock_access_token',
        refreshToken: 'mock_refresh_token',
        expireTime: Date.now() + 3600 * 1000 * 24, // 24小时后过期
        userInfo: {
          id: '1',
          name: '用户A',
          avatar: '',
          email: 'user@example.com'
        }
      };
      
      // 保存Token
      await tokenManager.setToken(
        mockResponse.accessToken,
        mockResponse.refreshToken,
        mockResponse.expireTime
      );
      
      // 保存用户信息
      await tokenManager.setUserInfo(mockResponse.userInfo);
      
      // 跳转到首页
      // router.pushUrl({ url: 'pages/Home' });
    } catch (error) {
      console.error('登录失败:', error);
    }
  }
  
  async onLogout() {
    // 清除Token
    await tokenManager.clearToken();
    
    // 跳转到登录页
    // router.pushUrl({ url: 'pages/Login' });
  }
  
  async checkLoginStatus() {
    const isLoggedIn = await tokenManager.isLoggedIn();
    if (!isLoggedIn) {
      // 跳转到登录页
      // router.pushUrl({ url: 'pages/Login' });
    }
  }
  
  build() {
    Column({ space: 16 }) {
      TextInput({ placeholder: '用户名' })
        .onChange((value: string) => this.username = value)
      
      TextInput({ placeholder: '密码' })
        .type(InputType.Password)
        .onChange((value: string) => this.password = value)
      
      Button('登录')
        .onClick(() => this.onLogin())
      
      Button('退出登录')
        .onClick(() => this.onLogout())
    }
    .width('100%')
    .height('100%')
    .padding(16)
    .justifyContent(FlexAlign.Center)
  }
}

设计要点:

  • 登录时保存Token
  • 退出时清除Token
  • 检查登录状态

本章小结

核心知识点

本文完成了安全存储封装:

1. 安全存储API

  • 统一存储接口
  • 支持多种数据类型
  • 错误处理和日志记录

2. 加密存储服务

  • XOR加密算法
  • Base64编码
  • 加密对象存储

3. Token管理器

  • Token存储和管理
  • Token过期检查
  • Token刷新机制

相关链接

相关推荐
阿狸猿3 小时前
网络安全体系设计
安全·web安全
大鱼>3 小时前
AIoT安全攻防:当物联网设备成为黑客后门
人工智能·物联网·安全·aiot
HackTwoHub3 小时前
免费FOFA高级会员、DayDaymap、360Quake、Hunter测绘搜索引擎高级会员免费使用最大1W条查询工具
运维·安全·web安全·搜索引擎·网络安全·系统安全·安全架构
视觉&物联智能3 小时前
【杂谈】- AI落地加速,安全亟待补位:企业智能转型的安全突围之道
人工智能·安全
网络研究院3 小时前
网络研究观-严重漏洞允许以 root 用户身份执行任意命令:CVE-2026-0273 分析
网络·安全·漏洞·修复·设备
爱和冰阔落3 小时前
【MCP实战】从0写一个本地工具服务器:文件搜索、SQLite查询与安全边界
服务器·安全·sqlite
杨先生哦3 小时前
【2026 热端攻防系列 2/12】DOM 型 XSS 深度实战:AI 多态变形免杀 + 全维度防御
前端·人工智能·笔记·安全·web安全·xss
暗黑小白4 小时前
第八篇:人在回路与内容安全 —— 当 AI 说“让我请示一下“
python·安全·架构·ai agent
数据知道5 小时前
指纹浏览器:DNS 泄漏防范与 WebRTC 本地 IP 屏蔽的底层实现
爬虫·网络协议·tcp/ip·安全·webrtc·数据采集·指纹浏览器