--- name: itfx-prefix-script description: >- Generates InterfaceForward (接口通/itfx) PrefixScript C# for any signature/auth algorithm (MD5/SHA/HMAC/RSA/custom). Maps writes to Query/Header/Body field lists per downstream interface detail. Use for 前置脚本, PrefixScript, Natasha script. --- # itfx 接口映射前置脚本生成(通用签名) 为 **InterfaceMap → PrefixScript** 生成 Natasha 可执行的 C# 方法体,**按用户描述的任意签名规则**实现,不限于某一服务商。 ## 何时使用 - 生成/修改 **前置脚本**(非 PostfixScript) - 第三方 API 需要签名、鉴权头、动态 token、时间戳参与计算等 - OMS「上游接口 → 映射明细 → 前置脚本」 ## 执行时机 ``` InParamRestructure → ReplacePlaceholder → PrefixScript ← 本 Skill → SerializationBody → HTTP 请求 → PostfixScript ``` | 可用 | 不可用 | |------|--------| | `obj.OriginalInterfaceMappedInput` | `obj.TargetInterfaceInput`(未序列化) | | `obj.QueryFieldList` / `HeaderFieldList` / `FormFieldList` | `obj.TargetInterfaceOutput` | | `obj.FixedFieldList` | 改 `InParamTreeList` / 映射树 | | `obj.GetAccountFieldValueOrNull(...)` | | ## 下游接口明细 ↔ 运行时(写入位置必对齐) | 接口明细 Tab | 运行时列表 | 典型用途 | |--------------|------------|----------| | Query KV | `obj.QueryFieldList` | URL 参数、签名字段 | | Header KV | `obj.HeaderFieldList` | 请求头、Authorization | | Body KV(FormUrlEncoded) | `obj.FormFieldList` | 表单字段 | | Mapping Body 树 | → `OriginalInterfaceMappedInput` | 映射体(Form/None 类) | | Request Body 树(Json/Xml) | → `OriginalInterfaceMappedInput` | JSON/XML Body | **ContentType:** `None(0)` 无 Body;`FormUrlEncoded(1)` Body=FormFieldList;`Json(3)`/`Xml(4)` Body=序列化映射体。 ## 占位符(ReplacePlaceholder 早于 PrefixScript) | 占位符 | PrefixScript 阶段 | |--------|-------------------| | `$TimeStamp` / `$DateTime` / `$FromAccount` / `$Url` / `$token` | 通常已替换,直接读字段 Value | | `$FromMappingBody` | FormUrlEncoded:可能仍为字面量;None/Json Query:通常已替换为 JSON。**签名用最终 HTTP 参数值,不要用占位符字面量** | ## 脚本技术约束 - C#,Natasha 动态编译;参数名 **`obj`**(不是 `context`) - 仅方法体:无 `using` / `class` / `namespace` / `void Main` - 类型 **全限定名**:`System.Security.Cryptography.HMACSHA256`、`Newtonsoft.Json.JsonConvert` - 含 `await` 或首行 `//async` → 异步委托 --- ## 通用签名生成流程(任何算法都走此流程) 用户未给全规则时,**先向用户确认**缺失项;给全则直接生成。 ### 1. 从需求提取七要素 | 要素 | 问什么 | |------|--------| | 算法 | MD5 / SHA1 / SHA256 / HMAC-SHA1 / HMAC-SHA256 / HMAC-MD5 / RSA / 自定义 | | 签名字符串 | 字段如何拼接、排序、分隔符、是否含 urlPath/body/secret 前缀后缀 | | 参与字段 | 哪些 Query/Header/Form/Body JSON 字段;**排除签名字段本身** | | 密钥 | 账户字段名(`GetAccountFieldValueOrNull`)或固定参数 | | 输出位置 | Query / Header / Form Body / JSON Body 字段名 | | 编码 | UTF-8;hex **大写/小写**、Base64、是否 URL 编码 | | 特殊 | 空值是否跳过、嵌套 JSON、时间戳单位、是否 double-MD5 等 | ### 2. 对照下游接口明细 - 签名字段在哪个 Tab → 写入 `QueryFieldList` / `HeaderFieldList` / `FormFieldList` 或改 `OriginalInterfaceMappedInput` - 参与签名的业务字段在哪个 Tab → 从**对应列表**读取 Value - 账户字段名与服务商配置一致 ### 3. 取参与签名的参数值(通用原则) **原则:签名字符串中的每个值 = 最终 HTTP 请求里该参数的值。** ```csharp // 按名读 Query(通用 helper 思路,可内联) string GetQueryValue(string name) { for (int i = 0; i < obj.QueryFieldList.Count; i++) if (obj.QueryFieldList[i].Name == name) return obj.QueryFieldList[i].Value ?? ""; return ""; } // Header / Form 同理遍历 HeaderFieldList / FormFieldList // JSON Body(ContentType.Json) string bodyJson = Newtonsoft.Json.JsonConvert.SerializeObject(obj.OriginalInterfaceMappedInput); // 映射体 JSON:优先读已替换好的 Query/Form 字段;仍为 $FromMappingBody 时再 SerializeObject ``` **常见坑(任何服务商):** - 签名字段(`sign`、`_aop_signature` 等)**不参与**签名字符串 - 不要读错列表(Query 参数却在 FormFieldList 里找) - 不要对已是最终值的字段再 `SerializeObject`(字段顺序/空格会变) - JSON 内 `"CHINA SHENZHEN"` 与 URL 编码的 `+` **不是同一字符**,签名用 JSON 原文 - FormUrlEncoded 阶段 `$FromMappingBody` 可能尚未替换,需 `SerializeObject(OriginalInterfaceMappedInput)` 或读已替换 Value ### 4. 常见算法 C# 片段(按文档选用,勿臆造) **MD5 / SHA → hex** ```csharp var md5 = System.Security.Cryptography.MD5.Create(); byte[] hash = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(signStr)); string sign = System.BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); // 文档要求大写则 .ToUpperInvariant() ``` **HMAC-SHA1 / HMAC-SHA256 → hex** ```csharp using (var hmac = new System.Security.Cryptography.HMACSHA256(System.Text.Encoding.UTF8.GetBytes(secret))) { byte[] hash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(signStr)); string sign = System.BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); } // HMACSHA1 替换 HMACSHA256;文档要 Base64 则 Convert.ToBase64String(hash) ``` **按键排序后 key+value 拼接(多种开放平台通用)** ```csharp var parts = new System.Collections.Generic.List(); // parts.Add("fieldA" + valueA); 排除 sign 字段 parts.Sort(System.StringComparer.Ordinal); var sb = new System.Text.StringBuilder(); for (int i = 0; i < parts.Count; i++) sb.Append(parts[i]); string signStr = sb.ToString(); // 或 prefix + sb + suffix,按文档 ``` **secret 包裹式:`secret + params + secret`(1688 等)** ```csharp string signStr = secret + paramString + secret; ``` **写入 Query / Header / Form(字段名按接口明细)** ```csharp for (int i = 0; i < obj.QueryFieldList.Count; i++) { if (obj.QueryFieldList[i].Name == "sign") { obj.QueryFieldList[i].Value = sign; break; } } ``` ### 5. 生成前自检 - [ ] 算法、拼接规则、hex 大小写与**用户/文档**一致(不默认某一种) - [ ] 读写列表与接口明细 Tab 一致 - [ ] 签名字段未参与计算 - [ ] 仅用 `obj`、全限定名、无 `using` - [ ] 空密钥有抛错或明确处理 --- ## 示例(仅参考,生成时以用户需求为准) ### 示例 A:HMAC-SHA1 + urlPath + 排序 param(阿里国际站 AOP 一类) - 拼接:`urlPath + sort(key+value)`,不含签名字段 - 输出:Query `_aop_signature`,小写 hex - 参数在 **QueryFieldList**;JSON 优先读 `notifyBizEventDTO` 的 Value ### 示例 B:MD5 + secret 包裹 + 排序 keyvalue(1688 一类) - 拼接:`appSecret + key1val1key2val2... + appSecret` - 输出:Query/Form 的 `sign`,常为大写 hex ### 示例 C:HMAC-SHA256 + Header - Body 用 `SerializeObject(OriginalInterfaceMappedInput)` 参与拼接 - 输出:`HeaderFieldList` 的 `X-Sign` 或 `Authorization` ### 示例 D:JSON Body 内写字段 - `obj.OriginalInterfaceMappedInput["sign"] = sign;` 更多代码模式见 [examples.md](examples.md)。 --- ## 输出要求 只输出一个 ```csharp 代码块(Natasha 方法体),不要额外解释。 ## 禁止事项 - 不要用 `HttpClient` 再发请求 - 不要改 `InParamTreeList` / `OutParamTreeList` - 不要用 `TargetInterfaceInput` - 不要输出 Python/JavaScript - 不要写 `using`;不要用 `context` - 不要把 Query/Header 签名写到 `FormFieldList` - **不要**在未确认文档时硬编码某一服务商的算法(如默认阿里 HMAC-SHA1) - 不要把签名字段放进待签名字符串