# 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.统一的下载功能