Files
simapi-cj/src/helpers/simapi_aes_body_checker.cj
T

115 lines
3.7 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (c) 2025 SimcuTeam. All rights reserved.
* 遵循 MIT 许可证。
*/
package simcu::simapi.helpers
import std.collection.*
import std.io.*
import simcu::serialization.*
import soulsoft_web_http.*
import simcu::simapi.communications.*
import simcu::simapi.exceptions.*
import simcu::simapi.interfaces.*
/**
* AES body 请求({"data": "密文"})。
*/
public class AesBodyRequest {
public var data: String = ""
}
/**
* 服务端 AES body 解密校验器。
*
* 仓颉无 ModelBinder 机制,按项目惯例由控制器在方法开头调用:
* let jsonStr = SimApiAesBodyChecker.decryptBody(context, provider)
* let request = JsonSerializer.deserialize<XxxRequest>(jsonStr)
* 或标注 @AesBody 注解自动执行(SimApiRequestDelegateFactory)。
*
* 流程:
* 1. 读取 body 并反序列化为 {"data": "密文"}
* 2. 校验 Data 非空
* 3. 提取 appIdQuery/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<AesBodyRequest>(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<Byte>(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)
}
}