使用Java 11开发无服务器Discord机器人时交互验证失败求助
排查Discord无服务器机器人交互验证失败的问题
我来帮你捋捋Java 11下Discord交互验证失败的可能原因,结合你的代码和Discord的验证规则,有几个关键细节很容易被忽略:
1. 公钥与签名的字节转换是否准确
你的fromHex方法逻辑看起来没问题,但要注意两个关键点:
- 确认你复制的应用公钥没有多余的空格、换行或字符缺失——Discord提供的公钥是纯十六进制字符串,任何格式错误都会直接导致验证失败。可以做个小测试:把公钥转成字节数组后再转回十六进制,看是否和原字符串完全一致。
- 检查你使用的
Crypto库的authKey方法是否正确将十六进制公钥解析为Ed25519格式的公钥。Discord的签名算法是Ed25519,有些库可能要求公钥是特定的对象格式,而非直接传入字节数组,这一步如果出错,后续验证必然失败。
2. 待验证消息的拼接逻辑有误
Discord要求的待验证内容是**X-Signature-Timestamp字符串的字节 + 请求体的原始字节**,你的代码里用了x_signature_timestamp + event.get("body")再转字节,这里可能存在两个问题:
- 如果
event.get("body")是经过解析或转码后的字符串(比如API Gateway的请求体如果是Base64编码的,直接调用getBody()会得到Base64字符串而非原始内容),转字节后会和Discord签名时用的原始请求体不一致。 - 字符串拼接后再转字节,可能因为编码问题(比如默认编码不是UTF-8)导致字节数组和预期不符。
修正后的拼接逻辑应该直接操作字节:
// 处理API Gateway可能的Base64编码请求体 byte[] bodyBytes; if (event.getIsBase64Encoded()) { bodyBytes = Base64.getDecoder().decode(event.getBody()); } else { bodyBytes = event.getBody().getBytes(StandardCharsets.UTF_8); } // 时间戳转UTF-8字节 byte[] timestampBytes = x_signature_timestamp.getBytes(StandardCharsets.UTF_8); // 合并时间戳和请求体字节 byte[] messageBytes = ByteBuffer.allocate(timestampBytes.length + bodyBytes.length) .put(timestampBytes) .put(bodyBytes) .array();
3. 验证方法的参数顺序是否匹配
不同的加密库对验证方法的参数顺序要求可能不同。Discord的验证逻辑是:用应用公钥,验证timestamp+body的字节是否匹配X-Signature-Ed25519的签名。要确认你的Crypto.authVerify方法参数顺序是否正确——比如有些库的方法签名是authVerify(签名字节, 消息字节, 公钥),如果你的调用顺序搞反了,肯定会验证失败。
4. 请求头的获取是否正确
HTTP请求头是不区分大小写的,但部分框架(比如AWS API Gateway)会将请求头转为小写存储。确保你获取X-Signature-Timestamp和X-Signature-Ed25519时用的是正确的key,比如:
String x_signature_timestamp = event.getHeaders().get("x-signature-timestamp"); String x_signature_ed25519 = event.getHeaders().get("x-signature-ed25519");
额外注意事项
- 绝对不要修改请求体的原始内容:哪怕是去掉一个空格或换行,都会导致签名不匹配。
- 测试时可以手动构造请求:把Discord发送的验证请求的时间戳、原始body、签名保存下来,本地跑验证逻辑,看是否能通过,这样可以快速定位是代码问题还是环境问题。
内容的提问来源于stack exchange,提问作者Piotr Majewski
相关产品推荐
相关产品推荐

