/* * Copyright (c) 2025 SimcuTeam. All rights reserved. * 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。 */ package simapi.helpers import std.collection.* import std.io.* import simapi_serialization.* import soulsoft_web_http.* import simapi.communications.* import simapi.exceptions.* import simapi.interfaces.* /** * AES body 请求({"data": "密文"},对齐 C# SimApiOneFieldRequest)。 */ public class AesBodyRequest { public var _data: String = "" } /** * 服务端 AES body 解密校验器(对齐 C# ModelBinders/AesBodyModelBinder)。 * * 仓颉无 ModelBinder 机制,按项目惯例由控制器在方法开头调用: * let jsonStr = SimApiAesBodyChecker.decryptBody(context, provider) * let request = JsonSerializer.Deserialize(jsonStr) * 或标注 @AesBody 注解自动执行(SimApiRequestDelegateFactory)。 * * 流程(与 C# 一致): * 1. 读取 body 并反序列化为 {"data": "密文"} * 2. 校验 Data 非空 * 3. 提取 appId(Query/Header) * 4. provider.getKey(appId) 获取密钥 * 5. SimApiAesUtil.decrypt 解密得到明文 JSON 字符串 * 返回解密后的 JSON 字符串,由控制器按目标类型反序列化。 */ public class SimApiAesBodyChecker { private init() {} /** * 解密请求体,返回明文 JSON 字符串。 * @param context 当前请求上下文。 * @param provider AES 密钥提供器。 * @return 解密后的 JSON 字符串。 */ public static func decryptBody(context: HttpContext, provider: AesBodyProviderBase): String { // 1. 读取 body let body = readBody(context) if (body.isEmpty()) { SimApiError.error(code: 400, message: "请求体不能为空") } // 2. 反序列化 {"data": "密文"} let req = JsonSerializer.Deserialize(body) if (req._data.isEmpty()) { SimApiError.error(code: 400, message: "请求体缺少密文Data字段") } // 3. 提取 appId var appId: ?String = None if (let Some(name) <- provider.appIdName) { if (!name.isEmpty()) { appId = getParam(context, name) if (appId == None || appId == Some("")) { SimApiError.error(code: 400, message: "未找到${name}") } } } // 4. 获取密钥 let key = provider.getKey(appId) if (key == None || key == Some("")) { SimApiError.error(code: 400, message: "获取密钥失败(应用不存在或密钥未配置)") } // 5. 解密 let jsonStr = SimApiAesUtil.decrypt(req._data, key.getOrThrow()) if (jsonStr.isEmpty()) { SimApiError.error(code: 400, message: "解密失败") } jsonStr } private static func readBody(context: HttpContext): String { try { context.request.enableBuffering() var buffer = Array(4096, repeat: 0) var sb = StringBuilder() var read = context.request.body.read(buffer) while (read > 0) { sb.appendFromUtf8(buffer.slice(0, read)) read = context.request.body.read(buffer) } // 重置流位置,供后续业务读取 if (let seekable: Seekable <- context.request.body) { seekable.seek(SeekPosition.Begin(0)) } sb.toString() } catch (ex: Exception) { SimApiError.error(code: 400, message: "读取请求体失败: ${ex.message}") } "" } private static func getParam(context: HttpContext, name: String): ?String { let q = context.request.query.get(name) if (q != None && q != Some("")) { return q } context.request.headers.get(name) } }