s***@aliyun.com
s***@aliyun.com
  • 发布:2026-06-13 09:35
  • 更新:2026-06-13 09:35
  • 阅读:35

客户端网络请求(SM2签名,SM4加密)与java服务端之前加密通信

分类:uni-app x

sunrains-request

注:客户端与服务端之间通信 数据 加密,签名,保证数据安全与防串改(完整功能依赖付费插件sunrains-smutil)

客户端与服务端(java)之间通信数据加密、签名,保证数据安全与防篡改
支持平台:微信小程序、H5、Android、iOS、鸿蒙
请求方式支持:GET、POST(application/json、application/x-www-form-urlencoded、multipart/form-data(计划中))

服务端(java)点击 sunrains-java


模块目录结构

sunrains-request/  
├── index.uts                          # 模块入口,统一导出  
├── js_sdk/  
│   ├── inutils/  
│   │   └── requestApi.uts             # 底层网络请求封装(uni.request / uni.uploadFile)  
│   └── openutil/  
│       ├── base64Util.uts             # Base64 编解码工具  
│       ├── randomUtil.uts             # 随机字符串生成  
│       └── request/  
│           ├── Request.uts            # 请求入口(ReqApi)  
│           └── reqHandle/  
│               ├── ReqHandle.uts      # 请求处理抽象基类  
│               ├── handle.uts         # 策略工厂(根据 reqFlag 选择 Handler)  
│               └── handle/  
│                   ├── VoidHandle.uts           # 无加密无签名模式  
│                   ├── SignHandle.uts           # 仅签名模式  
│                   ├── EncryptHandle.uts        # 仅SM4加密模式  
│                   └── SignWithEncryptHandle.uts # 签名+SM4加密模式

导出 API 一览

导出函数 来源文件 说明
ReqApi request/Request.uts 高层请求入口,自动根据 reqFlag 完成签名/加密/发送/解密
RequestApi inutils/requestApi.uts 底层请求入口,直接发起 uni.request 并返回 Promise\<Response>
uploadFile inutils/requestApi.uts 文件上传,返回 Promise\<Response>
stringToBase64 openutil/base64Util.uts 字符串 → Base64 编码
base64ToString openutil/base64Util.uts Base64 → 字符串解码
random openutil/randomUtil.uts 生成指定长度的随机字符串(字母+数字),默认16位

网络请求架构(核心)

整体流程

调用方  
  │  
  ▼  
ReqApi(frontVo: FrontRequestVo)          ← 高层入口  
  │  
  ├─ 1. selectHandle(reqFlag)            ← 根据模式选择 Handler  
  │  
  ├─ 2. handle.handleHeaderAndBody(vo)   ← 构造请求头 + 处理请求体(签名/加密)  
  │  
  ├─ 3. RequestApi(req)                  ← 底层发起 HTTP 请求  
  │  
  └─ 4. handle.handleRes(req, res)       ← 处理响应(解密/验签)  
  │  
  ▼  
返回 Response

四种请求模式(ReqFlag)

通过 FrontRequestVo.reqFlag 指定,决定请求的安全处理策略:

reqFlag Handler 签名 请求体SM4加密 响应SM4解密 需要priKey 需要pubKey
"void" VoidHandle
"sign" SignHandle ✓ SM2签名
"sm4Encrypt" EncryptHandle ✓ SM4加密 ✓ SM4解密
"signwithenSM2-Sm4Encrypt" SignWithEncryptHandle ✓ SM2签名 ✓ SM4加密 ✓ SM4解密

请求头处理(所有模式共用)

无论哪种模式,handleSignHeader 都会执行以下操作:

  1. SM2加密SM4密钥:生成随机 SM4 key + iv,用服务端公钥(pubKey)通过 SM2 加密后放入 header["key"]
  2. 条件签名(sign / signwithenSM2-Sm4Encrypt 模式):
    • 生成 timestamp(时间戳)和 nonceStr(16位随机串)
    • 将所有业务参数 + timestamp + nonceStr 按 key 字典序排列,拼接为 key=value& 格式
    • 使用客户端私钥(priKey)对拼接字符串进行 SM2 签名
    • 签名结果放入 header["sign"],同时放入 header["timestamp"]header["nonceStr"]

请求体处理(按 HTTP Method + contentType)

GET 请求:

  • void / sign 模式:参数拼接到 URL query string
  • sm4Encrypt / signwithenSM2-Sm4Encrypt 模式:参数 JSON 序列化后 SM4 加密,拼接到 ?data=加密结果

POST 请求(按 contentType):

contentType 处理方式
"json" Content-Type: application/json,加密模式下 body 整体 SM4 加密为 {"data":"加密结果"}
"urlencoded" Content-Type: application/x-www-form-urlencoded,加密模式下 data=加密结果
"form-data" Content-Type: multipart/form-data(暂未实现加密)

响应处理

  • 无加密模式(void / sign):直接返回 Response
  • 加密模式(sm4Encrypt / signwithenSM2-Sm4Encrypt):
    1. 先校验外层 res.code == resSuccessCode()(当前定义为 0
    2. 取出 res.data(SM4 加密的字符串)
    3. 用本次请求生成的 SM4 key + iv 解密
    4. 解密结果为内层 Response,返回给调用方

底层请求(RequestApi)

RequestApi(req: RequestVo) → Promise<Response>
  • 基于 uni.request 封装
  • 超时时间:req.timeout ?? 6000ms
  • 成功(statusCode=200):resolve Response 对象
    • Android 平台特殊处理:通过 JSON.parse(JSON.stringify(res.data)) 转换
    • 其他平台:直接 res.data as Response
  • 失败(非200):reject CustOtherFailError(包含 code + errorMsg)
  • 网络异常:reject CustOtherFailError(包含 errCode + errMsg)

文件上传(uploadFile)

uploadFile(req: UploadFileOptions) → Promise<Response>
  • 基于 uni.uploadFile 封装
  • 超时时间:req.timeout ?? 12000ms
  • 成功时解析 res.data 为 Response
  • 失败时 reject err.errMsg

类型定义(依赖 sunrains-common-type)

FrontRequestVo(前置请求参数)

type FrontRequestVo = {  
  method: 'GET' | 'POST',           // HTTP 方法  
  url: string,                       // 请求地址  
  data: UTSJSONObject | null,        // 请求数据  
  header: UTSJSONObject | null,      // 自定义请求头(会与签名/加密头合并)  
  reqFlag: ReqFlag,                  // 安全模式标识  
  priKey: string,                    // SM2 私钥(签名时使用)  
  pubKey: string,                    // SM2 公钥(加密SM4密钥时使用)  
  contentType: "json" | "urlencoded" | "form-data" | null,  // POST内容类型  
  timeout: number | null,            // 超时时间(ms)  
  filePathParamName: string | null   // 上传文件时,文件路径的参数名  
}

Response(统一响应)

type Response = {  
  success: boolean | null,    // 是否成功  
  msg: string | null,         // 提示信息  
  code: number,               // 状态码(0=成功)  
  errorMsg: string | null,    // 错误详情  
  data: any | null            // 业务数据(加密模式下为密文字符串)  
}

RequestVo(底层请求参数)

type RequestVo = {  
  url: string,  
  method?: 'GET' | 'POST' | null,  
  header: UTSJSONObject,  
  timeout?: number | null,  
  data: any  
}

Sm4Key(SM4密钥)

type Sm4Key = {  
  key: string,  
  iv: string  
}

错误处理(依赖 sunrains-common-err)

所有请求错误统一通过 UniError 抛出:

错误来源 errSubject errCode message
SM2私钥为空 SM2_PRI_KEY_BLANK 预定义码 预定义信息
SM2公钥为空 SM2_PUB_KEY_BLANK 预定义码 预定义信息
SM2签名失败 SM2_SIGN_FAIL 预定义码 预定义信息
HTTP非200 "请求失败" 服务端code errorMsg / msg
网络异常 "请求失败" err.errCode err.errMsg
加密模式响应码非0 "请求失败" res.code errorMsg / msg

调用方 catch 示例:

try {  
  const res = await ReqApi(frontVo)  
} catch (err) {  
  // 跨平台安全取值(避免 iOS 上 err.message 崩溃)  
  console.log("请求异常:", `${err}`)  
  // 或通过 err.message 获取错误信息(err 为 UniError 实例)  
}

工具函数

Base64 编解码

import { stringToBase64, base64ToString } from "@/uni_modules/sunrains-request"  

const encoded = stringToBase64("Hello")   // "SGVsbG8="  
const decoded = base64ToString("SGVsbG8=") // "Hello"

随机字符串

import { random } from "@/uni_modules/sunrains-request"  

const nonce = random(16)   // 生成16位随机字符串

依赖模块

模块 用途
sunrains-smutil SM2签名/加解密、SM4加解密
sunrains-common-type 类型定义(FrontRequestVo、Response、RequestVo、Sm4Key、ReqFlag)
sunrains-common-err 统一错误类(CustFailError、CustOtherFailError)

使用示例

1. 无加密请求(void)

import { ReqApi } from "@/uni_modules/sunrains-request"  

const frontVo: FrontRequestVo = {  
  method: "POST",  
  url: "http://your-api.com/test",  
  data: { name: "张三", age: 18 },  
  header: null,  
  reqFlag: "void",  
  priKey: "",  
  pubKey: "",  
  contentType: "json",  
  timeout: null,  
  filePathParamName: null  
}  
const res = await ReqApi(frontVo)

2. SM2签名请求(sign)

const frontVo: FrontRequestVo = {  
  method: "POST",  
  url: "http://your-api.com/test",  
  data: { name: "张三", age: 18 },  
  header: null,  
  reqFlag: "sign",  
  priKey: "客户端SM2私钥",  
  pubKey: "服务端SM2公钥",  
  contentType: "json",  
  timeout: null,  
  filePathParamName: null  
}  
const res = await ReqApi(frontVo)

3. 签名 + SM4加密请求(signwithenSM2-Sm4Encrypt)

const frontVo: FrontRequestVo = {  
  method: "POST",  
  url: "http://your-api.com/test",  
  data: { name: "张三", age: 18 },  
  header: null,  
  reqFlag: "signwithenSM2-Sm4Encrypt",  
  priKey: "客户端SM2私钥",  
  pubKey: "服务端SM2公钥",  
  contentType: "json",  
  timeout: null,  
  filePathParamName: null  
}  
const res = await ReqApi(frontVo)  
// res.data 已自动解密

4. 文件上传(计划中)


0 关注 分享

要回复文章请先登录注册