# efAxios **Repository Path**: yunkss/efAxios ## Basic Information - **Project Name**: efAxios - **Description**: 初始化codelab项目 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-05-26 - **Last Updated**: 2025-06-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README
# 介绍 > 本篇Codelab基于eftool1.1.11能力,实现efAxios前后端请求以及传输加解密,主要完成以下功能: > 1.使用efAxios进行整体传输加解密 > 2.使用efAxios进行关键字加解密 > 3.使用efAxios进行响应内容转换 > 4.统一的上传功能 > 5.统一的下载功能 ## 相关概念 * efAxios:三方库eftool进行二次封装axios的产物(eftool为本人开发,不涉及抄袭)。 * 加密逻辑:数据加解密采用国密SM4,而SM4的key又使用SM2进行了加密,每次请求的时候都会重新生成SM4的key保证数据传输的安全 * 数据完整性校验:每次发起请求时将求参数进行SM3的摘要签名,传输到后端时进行验签,如果传输过程中数据被篡改将验签失败 ## 完整示例 [gitee源码地址](https://gitee.com/yunkss/efAxios) ## 源码下载 [efAxios.zip](http://aliyunmr.com:12306/efAxios.zip)
# 环境搭建 > 我们首先需要完成HarmonyOS开发环境搭建,可参照如下步骤进行。 ## 软件要求 * DevEco Studio版本:DevEco Studio NEXT Developer Preview2。 * HarmonyOS SDK版本:API version 11。 ## 硬件要求 * 设备类型:华为手机或运行在DevEco Studio上的华为手机设备模拟器。 * HarmonyOS系统:HarmonyOS NEXT Developer Preview2。 ## 环境搭建 * 安装DevEco Studio,详情请参考下载和安装软件。 * 设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境: * 如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。 * 如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。 * 开发者可以参考以下链接,完成设备调试的相关配置: [使用真机进行调试](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/ide_debug_device-0000001053822404-V2) [使用模拟器进行调试](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/ide_debug_emulator-0000001115721921-V2)
# 代码结构解读 > 本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在源码下载或gitee中提供。 ``` ├──entry/src/main/ets // 代码区 │ ├──efAxios │ │ ├──AxiosUtil.ets //二次封装的axios代码 │ │ └──EfClientApi.ets //二次封装的请求 │ ├──entryability │ │ └──EntryAbility.ets // 程序入口类 │ └──pages │ └───Index.ets // demo页面 └──entry/src/main/resources // 资源文件目录 ```
# 请求拦截 > 1.在请求头添加防篡改字段的随机数因子nonce > 2.在请求头添加防重放的随机数因子timestamp > 3.在请求头添加token,如果传入的话config.headers[efAxiosParams.tokenName]=efAxiosParams.tokenValue; > 4.如果开启传输加密,在请求头添加ef_random_key,为SM2将SM4的key加密后的字符串 > 5.如果开启传输加密,在请求头添加sign,将请求参数使用SM3进行签名 > 6.如果开启整体传输加密isAllEncrypt则将data请求参数全部使用SM4加密 > 7.如果开启关键字传输加密isPartEncrypt则将data请求参数与关键字集合efAxiosParams.keyWordsList对比使用SM4加密 ``` //1.防篡改字段的随机数因子 let nonce = RandomUtil.randomStrBySize(16); //1.防篡改字段的随机数因子 config.headers.nonce = nonce; //2.防重放的随机数因子 config.headers.timestamp = new Date().getTime(); //3.有token值说明登录成功则添加到请求头 if (efAxiosParams.tokenValue) { //token鉴权值 config.headers[efAxiosParams.tokenName] = efAxiosParams.tokenValue; } //步骤4.5.6.7详见源码 ```
# 响应拦截 > 1.如果开启isAllEncrypt整体传输加密,则使用传回的SM4key进行整体解密 > 2.如果开启isPartEncrypt关键字传输加密,则使用传回的SM4key与efAxiosParams.keyWordsList对比进行关键字解密 > 3.如果开启响应内容转换OutDTO对象isConvertDTO,则将响应解雇转换为统一的OutDTO ``` //判断如果有不加解密的接口则跳过加密 let lastUrl: string = response.config.url?.slice(response.config.url?.lastIndexOf("/") + 1, response.config.url.length).trim() || ""; if (response.status === 200) { //1.整体解密 if (efAxiosParams.isAllEncrypt) { //判断如果有不加解密的接口则跳过加密 if (efAxiosParams.ignoreEncryptList.indexOf(lastUrl) < 0) { response.data = await AxiosUtil.decryptResponseData(response.data); } } //2.关键字解密 if (efAxiosParams.isPartEncrypt) { response.data = await AxiosUtil.decryptPartResponseData(response.data); } if (efAxiosParams.isConvertDTO) { //3.将响应数据转换 response.data = AxiosUtil.convertResponseDTO(response); } } else { ToastUtil.showToast('接口返回错误~'); } ```
# 上传功能 > 1.上传ArrayBuffer数据 > 2.上传uri格式数据 > 3.上传进度加载 ``` let formData = new FormData(); if (keyName) { if (isUri) { formData.append(keyName, 'internal://cache/' + uri, { fileName: fileName }); } else { formData.append(keyName, data, { fileName: fileName }); } } else { if (isUri) { formData.append('file', 'internal://cache/' + uri, { fileName: fileName }); } else { formData.append('file', data, { fileName: fileName }); } } //需要添加的header对象 let addHead: AxiosHeaders = new AxiosHeaders(); addHead.set('Content-Type', 'multipart/form-data', true); addHead.set('Accept', '*/*', true); addHead.set('Req-Type', 'uploadOrDownload', true); return await efAxios.post, FormData>(url, formData, { headers: addHead, context: getContext(this) as common.UIAbilityContext, onUploadProgress: (progressEvent: AxiosProgressEvent): void => { if (progressEvent && progressEvent.loaded && progressEvent.total) { //回调进度 progressCallBack(Math.ceil(progressEvent.loaded / progressEvent.total * 100)); } } }).then((response: AxiosResponse) => { return response.data }).catch((err: AxiosError) => { ToastUtil.showToast(err.message); }); ```
# 下载功能 > 1.下载服务器端文件 > 2.下载进度加载 ``` //获取上下文 let context = getContext(this) as common.UIAbilityContext; let filesDir = context.filesDir; //组装下载路径 let finalPath = filesDir + "/" + filePath; try { let isExists = fs.accessSync(finalPath); //如果已存在则删除 if (isExists) { fs.unlinkSync(finalPath); } } catch (err) { ToastUtil.showToast('删除路径下已存在文件失败:' + filePath); return; } //需要添加的header对象 let addHead: AxiosHeaders = new AxiosHeaders(); addHead.set('Req-Type', 'uploadOrDownload', true); //下载 return await efAxios({ url: url, method: 'get', context: context, filePath: finalPath, headers: addHead, onDownloadProgress: (progressEvent: AxiosProgressEvent): void => { if (progressEvent && progressEvent.loaded && progressEvent.total) { //回调进度 progressCallBack(Math.ceil(progressEvent.loaded / progressEvent.total * 100)); } } }).then((response: AxiosResponse) => { return response.data }).catch((err: AxiosError) => { ToastUtil.showToast(err.message); }); ```
# 总结 > 您已经完成了本次Codelab的学习,并了解到以下知识点: > 1.使用efAxios进行整体传输加解密 > 2.使用efAxios进行关键字加解密 > 3.使用efAxios进行响应内容转换 > 4.统一的上传功能 > 5.统一的下载功能