Android与ASP.NET Core中GET/POST请求签名生成与验证问题排查
从你描述的现象来看,单参数签名验证正常、多参数失败,确实很大概率和查询字符串的编码处理、参数排序有关,尤其是你怀疑的&符号编码问题。下面我帮你拆解可能的问题点,以及对应的解决方案:
一、核心问题排查方向
1. &符号被错误编码为&
你提到的&是HTML实体编码后的结果,如果Android端生成查询字符串时不小心做了HTML转义,就会把分隔符&变成&。比如原本应该是param1=1¶m2=2,实际生成的是param1=1&param2=2,这会导致.NET端解析到的参数或者签名源字符串完全不符合预期,自然验证失败。
2. 参数排序不一致
签名生成的核心要求是两端使用完全相同的源字符串,如果Android端生成查询字符串时参数顺序是随机的(比如HashMap的遍历顺序),而.NET端是按参数名字典序排序后生成签名源,那么多参数时两端的源字符串就会不一样,验证必然失败。
3. URL编码规则不统一
不同平台的URL编码细节可能有差异:比如空格是转成+还是%20,特殊字符(比如@、#)的处理是否一致。如果Android端和.NET端的编码规则不同,也会导致签名源字符串不匹配。
二、具体解决方案
1. 修复&符号的编码问题
确保生成查询字符串时,使用原始的&作为参数分隔符,不要做HTML转义。比如替换掉代码中可能存在的Html.escape()或者类似转义逻辑。
2. 强制统一参数排序
不管是Android端还是.NET端,都要按照参数名字典序(比如ASCII升序)对参数进行排序后再拼接成签名源字符串。这是签名验证的通用规范,能避免因参数顺序导致的不匹配。
3. 统一URL编码实现
使用标准的URL编码规则(RFC 3986),确保参数名和参数值的编码方式一致。Android端可以使用URLEncoder.encode()并指定编码为UTF-8;.NET端对应使用Uri.EscapeDataString()(如果.NET端之前用的是HttpUtility.UrlEncode(),注意它会把空格转成+,和Java的URLEncoder行为一致,需两端统一)。
三、Android端修正后的代码示例
假设你之前是直接遍历HashMap拼接字符串,下面是修正后的代码,包含排序、正确编码和分隔符处理:
import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; public class SignUtils { // 生成签名用的源字符串(多参数按字典序排序) public static String generateSignSource(Map<String, String> params) throws Exception { if (params == null || params.isEmpty()) { return ""; } // 1. 将参数名按字典序排序 List<String> sortedKeys = new ArrayList<>(params.keySet()); Collections.sort(sortedKeys); StringBuilder sb = new StringBuilder(); for (int i = 0; i < sortedKeys.size(); i++) { String key = sortedKeys.get(i); String value = params.get(key); // 2. 对参数名和值进行URL编码(遵循RFC 3986) String encodedKey = URLEncoder.encode(key, StandardCharsets.UTF_8.name()); String encodedValue = URLEncoder.encode(value, StandardCharsets.UTF_8.name()); // 3. 拼接,使用&分隔,注意不要转义& if (i > 0) { sb.append("&"); } sb.append(encodedKey).append("=").append(encodedValue); } return sb.toString(); } }
代码说明:
- 先对参数名排序,确保和.NET端的排序逻辑一致
- 使用
URLEncoder.encode()并指定UTF-8编码,和.NET端的编码规则对齐 - 直接使用
&作为分隔符,避免HTML转义
四、额外验证步骤
- 把Android端生成的签名源字符串打印出来,和.NET端生成的签名源字符串做对比,看是否完全一致
- 如果还是不一致,检查是否有隐藏的参数(比如.NET端自动添加的参数,或者Android端遗漏的参数)
- 确认签名算法(比如MD5、SHA256)的实现和密钥在两端完全一致
内容的提问来源于stack exchange,提问作者F. Aydemir

