在移动应用开发中,多环境API管理和接口安全是两个核心技术挑战。本文基于真实的鸿蒙项目,深入解析如何构建可扩展的多环境API架构和企业级安全签名体系。
一、技术背景与挑战
业务场景
某项目涉及房源、客源、人员等多个业务域,每个域都有独立的微服务API,且需要在开发、测试、预发布、生产四套环境中无缝切换。
核心挑战
- 多域名多环境管理:不同业务模块对应不同服务端口,环境切换复杂
- API安全防护:防止接口被恶意调用、参数篡改和重放攻击
- 开发效率:环境切换应透明化,不影响业务开发
二、多环境API架构设计
1. 分层架构设计
┌─────────────────────────────────────────┐
│ 业务层 │
│ (CustomerApiService, HouseApiService) │
├─────────────────────────────────────────┤
│ URL管理层 (AppUrl) │
├─────────────────────────────────────────┤
│ 环境配置层 (AppConstant) │
├─────────────────────────────────────────┤
│ 网络请求层 (HttpUtils) │
└─────────────────────────────────────────┘
2. 环境配置层实现
AppConstant.ets - 环境变量与密钥管理
// 环境控制:true=测试环境,false=生产环境
export const isTest = true
// 环境相关配置
export const EnvConfig = {
test: {
apiSecretKey: "",
mcId: ""
},
prod: {
apiSecretKey: "",
mcId: ""
}
}
// 动态获取当前环境配置
export const getCurrentConfig = () => isTest ? EnvConfig.test : EnvConfig.prod
// 业务域权限标识
export const BusinessDomain = {
NEW_HOUSE: "sy-xf", // 新房业务
TRANSACTION: "qy", // 交易管理
WORKFLOW: "wf", // 审批流程
REPORT: "sy-bb" // 数据报表
} as const
3. URL管理层实现
AppUrl.ets - 多业务域API地址管理
import { isTest } from "../constant/AppConstant"
export class AppUrl {
// 基础域名配置
private static readonly BASE_DOMAIN = isTest ? "bbb.t.com" : "bbb.com"
// 业务模块API地址
static readonly API_ENDPOINTS = {
// 主业务API
BASE: `http://${AppUrl.BASE_DOMAIN}/`,
// 客源管理服务
CUSTOMER: `http://${AppUrl.BASE_DOMAIN}:8011/`,
// 人员管理服务
PERSONNEL: `http://${AppUrl.BASE_DOMAIN}:8010/`,
// 房源管理服务
PROPERTY: `http://${AppUrl.BASE_DOMAIN}:8012/`,
// 公共服务
COMMON: `http://${AppUrl.BASE_DOMAIN}:8013/`
} as const
// 获取完整API地址
static getApiUrl(endpoint: keyof typeof AppUrl.API_ENDPOINTS, path: string): string {
return `${AppUrl.API_ENDPOINTS[endpoint]}${path}`
}
// 环境信息获取
static getEnvironmentInfo() {
return {
environment: isTest ? 'TEST' : 'PRODUCTION',
domain: AppUrl.BASE_DOMAIN,
endpoints: AppUrl.API_ENDPOINTS
}
}
}
4. 业务服务层调用示例
CustomerApiService.ets - 客源管理API
import { AppUrl } from './AppUrl'
export class CustomerApiService {
// 获取客源列表
static getCustomerList(params: any) {
const url = AppUrl.getApiUrl('CUSTOMER', 'api/customer/list')
return HttpUtils.get(url, params)
}
// 添加客源
static addCustomer(data: any) {
const url = AppUrl.getApiUrl('CUSTOMER', 'api/customer/add')
return HttpUtils.post(url, data)
}
}
三、企业级安全签名体系
1. 签名算法设计
基于MD5的参数签名算法,防止参数篡改和重放攻击:
AgentUtil.ets - 核心签名实现
import { JSONUtil, LogUtil } from "@pura/harmony-utils"
import { getCurrentConfig } from "../constant/AppConstant"
import { MD5Utils } from "@abner/net"
export class AgentUtil {
/**
* 生成ERP系统API安全签名
* @param paramsJson 请求参数JSON字符串
* @returns MD5签名字符串
*/
static getERPSignSecret(paramsJson: string): string {
try {
// 1. 参数排序与拼接
let paramStr = ""
const paramMap = JSONUtil.jsonToMap(paramsJson)
// 按key排序确保签名一致性
const sortedKeys = Array.from(paramMap.keys()).sort()
sortedKeys.forEach(key => {
const value = paramMap.get(key)
paramStr += `${key}=${value}&`
})
// 移除末尾&符号
paramStr = paramStr.slice(0, -1)
// 2. 构造签名字符串
const config = getCurrentConfig()
const signStr = `data${paramStr}secret${config.apiSecretKey}`
LogUtil.info("签名原始字符串", signStr)
// 3. MD5加密
const signature = MD5Utils.getInstance().hex_md5(signStr)
LogUtil.info("生成签名", signature)
return signature
} catch (error) {
LogUtil.error("签名生成失败", error)
throw new Error("签名生成失败")
}
}
/**
* 验证签名是否有效(客户端预验证)
* @param params 原始参数
* @param receivedSign 接收到的签名
* @returns 是否有效
*/
static verifySignature(params: any, receivedSign: string): boolean {
const paramsJson = JSON.stringify(params)
const expectedSign = AgentUtil.getERPSignSecret(paramsJson)
return expectedSign === receivedSign
}
}
2. 网络请求拦截器集成
RequestNetPlugin.ets - 请求签名拦截器
import { AgentUtil } from '../utils/AgentUtil'
export class RequestNetPlugin {
/**
* 请求前拦截器:自动添加签名
*/
static beforeRequest(config: any) {
// 添加时间戳防重放
config.data.timestamp = Date.now()
// 生成签名
const paramsJson = JSON.stringify(config.data)
const signature = AgentUtil.getERPSignSecret(paramsJson)
// 添加签名到请求参数
config.data.sign = signature
LogUtil.info("请求签名完成", { url: config.url, sign: signature })
return config
}
/**
* 响应拦截器:验证响应签名(可选)
*/
static afterResponse(response: any) {
// 可在此处验证服务端返回的签名
return response
}
}
3. 完整请求流程示例
// 业务调用示例
const addCustomer = async (customerData: any) => {
try {
// 1. 业务参数
const params = {
name: customerData.name,
phone: customerData.phone,
requirements: customerData.requirements
}
// 2. 自动签名(由拦截器处理)
const response = await CustomerApiService.addCustomer(params)
// 3. 处理响应
if (response.code === 200) {
ToastUtil.showToast("客户添加成功")
}
} catch (error) {
LogUtil.error("客户添加失败", error)
}
}
四、高级特性与优化
1. 环境切换热重载
// 开发期间支持动态切换环境
export class EnvManager {
static switchEnvironment(env: 'test' | 'prod') {
// 更新环境标识
PreferencesUtil.putSync('current_env', env)
// 清理缓存
this.clearApiCache()
// 重新初始化网络配置
HttpUtils.reinitialize()
ToastUtil.showToast(`已切换到${env}环境`)
}
private static clearApiCache() {
// 清理API响应缓存
}
}
2. 安全增强策略
export class SecurityUtils {
// RSA加密敏感参数
static encryptSensitiveData(data: string): string {
// RSA加密实现
return encryptedData
}
// 请求防重放(基于时间戳和nonce)
static generateNonce(): string {
return `${Date.now()}_${Math.random().toString(36)}`
}
// API限流控制
static rateLimitCheck(apiPath: string): boolean {
// 实现API调用频率控制
return true
}
}
3. 监控与日志
export class ApiMonitor {
// API调用统计
static trackApiCall(url: string, duration: number, success: boolean) {
const metrics = {
url,
duration,
success,
timestamp: Date.now(),
environment: isTest ? 'test' : 'prod'
}
// 上报到监控系统
LogUtil.info("API调用监控", metrics)
}
// 签名失败告警
static reportSignatureFailure(url: string, params: any) {
LogUtil.error("签名验证失败", { url, params })
// 发送告警通知
}
}
五、最佳实践与注意事项
1. 安全规范
- 密钥管理:生产环境密钥应通过安全渠道分发,避免硬编码
- 传输安全:必须配合HTTPS协议使用
- 签名算法:可根据安全要求升级为HMAC-SHA256或RSA
2. 性能优化
- 签名缓存:对相同参数的签名结果进行短时缓存
- 异步处理:签名计算放在工作线程中执行
- 请求合并:减少不必要的API调用
3. 开发效率
- 环境一键切换:通过配置中心或构建参数控制环境
- Mock数据:开发期间可使用Mock服务,减少对后端依赖
- API文档:维护完整的API文档和签名示例