feat(wxapi): 基于 ErroredResult 改造验签相关扩展方法

This commit is contained in:
Fu Diwei
2024-02-05 13:25:49 +08:00
committed by RHQYZ
parent 70611189cc
commit abf0a8f596
2 changed files with 78 additions and 39 deletions

View File

@@ -4,6 +4,8 @@ using System.Xml.Linq;
namespace SKIT.FlurlHttpClient.Wechat.Api
{
using SKIT.FlurlHttpClient.Primitives;
/// <summary>
/// 为 <see cref="WechatApiClient"/> 提供回调通知事件的扩展方法。
/// </summary>
@@ -176,15 +178,31 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
/// <param name="webhookNonce">微信回调通知中的 "nonce" 查询参数。</param>
/// <param name="webhookSignature">微信回调通知中的 "signature" 查询参数。</param>
/// <returns></returns>
public static bool VerifyEventSignatureForEcho(this WechatApiClient client, string webhookTimestamp, string webhookNonce, string webhookSignature)
public static ErroredResult VerifyEventSignatureForEcho(this WechatApiClient client, string webhookTimestamp, string webhookNonce, string webhookSignature)
{
if (client is null) throw new ArgumentNullException(nameof(client));
List<string> tmp = new List<string>(capacity: 3) { client.Credentials.PushToken!, webhookTimestamp, webhookNonce };
tmp.Sort(StringComparer.Ordinal);
ErroredResult result;
string sign = Utilities.SHA1Utility.Hash(string.Concat(tmp)).Value!;
return string.Equals(sign, webhookSignature, StringComparison.OrdinalIgnoreCase);
try
{
List<string> tmp = new List<string>(capacity: 3) { client.Credentials.PushToken!, webhookTimestamp, webhookNonce };
tmp.Sort(StringComparer.Ordinal);
string sign = Utilities.SHA1Utility.Hash(string.Concat(tmp)).Value!;
bool valid = string.Equals(sign, webhookSignature, StringComparison.OrdinalIgnoreCase);
if (valid)
result = ErroredResult.Ok();
else
result = ErroredResult.Fail(new Exception($"Signature does not match. Maybe \"{webhookSignature}\" is an illegal signature."));
}
catch (Exception ex)
{
result = ErroredResult.Fail(ex);
}
return result;
}
/// <summary>
@@ -197,25 +215,34 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
/// <param name="webhookJson">微信回调通知中请求正文JSON 格式)。</param>
/// <param name="webhookSignature">微信回调通知中的 "msg_signature" 查询参数。</param>
/// <returns></returns>
public static bool VerifyEventSignatureFromJson(this WechatApiClient client, string webhookTimestamp, string webhookNonce, string webhookJson, string webhookSignature)
public static ErroredResult VerifyEventSignatureFromJson(this WechatApiClient client, string webhookTimestamp, string webhookNonce, string webhookJson, string webhookSignature)
{
if (client is null) throw new ArgumentNullException(nameof(client));
ErroredResult result;
try
{
var encryptedEvent = client.JsonSerializer.Deserialize<InnerEncryptedEvent>(webhookJson);
return Utilities.WxMsgCryptor.VerifySignature(
InnerEncryptedEvent encryptedEvent = client.JsonSerializer.Deserialize<InnerEncryptedEvent>(webhookJson);
bool valid = Utilities.WxMsgCryptor.VerifySignature(
sToken: client.Credentials.PushToken!,
sTimestamp: webhookTimestamp,
sNonce: webhookNonce,
sMsgEncrypt: encryptedEvent.EncryptedData,
sMsgSign: webhookSignature
);
if (valid)
result = ErroredResult.Ok();
else
result = ErroredResult.Fail(new Exception($"Signature does not match. Maybe \"{webhookSignature}\" is an illegal signature."));
}
catch
catch (Exception ex)
{
return false;
result = ErroredResult.Fail(ex);
}
return result;
}
/// <summary>
@@ -228,27 +255,36 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
/// <param name="webhookXml">微信回调通知中请求正文XML 格式)。</param>
/// <param name="webhookSignature">微信回调通知中的 "msg_signature" 查询参数。</param>
/// <returns></returns>
public static bool VerifyEventSignatureFromXml(this WechatApiClient client, string webhookTimestamp, string webhookNonce, string webhookXml, string webhookSignature)
public static ErroredResult VerifyEventSignatureFromXml(this WechatApiClient client, string webhookTimestamp, string webhookNonce, string webhookXml, string webhookSignature)
{
if (client is null) throw new ArgumentNullException(nameof(client));
ErroredResult result;
try
{
XDocument xDoc = XDocument.Parse(webhookXml);
string? msgEncrypt = xDoc.Root?.Element("Encrypt")?.Value;
return Utilities.WxMsgCryptor.VerifySignature(
bool valid = Utilities.WxMsgCryptor.VerifySignature(
sToken: client.Credentials.PushToken!,
sTimestamp: webhookTimestamp,
sNonce: webhookNonce,
sMsgEncrypt: msgEncrypt!,
sMsgSign: webhookSignature
);
if (valid)
result = ErroredResult.Ok();
else
result = ErroredResult.Fail(new Exception($"Signature does not match. Maybe \"{webhookSignature}\" is an illegal signature."));
}
catch
catch (Exception ex)
{
return false;
result = ErroredResult.Fail(ex);
}
return result;
}
}

View File

@@ -5,32 +5,8 @@ namespace SKIT.FlurlHttpClient.Wechat.OpenAI
/// <summary>
/// 为 <see cref="WechatOpenAIClient"/> 提供回调通知事件的扩展方法。
/// </summary>
public static class WechatOpenAIClientEventExtensions
public static partial class WechatOpenAIClientEventExtensions
{
private static TEvent InnerDeserializeEventFromXml<TEvent>(this WechatOpenAIClient client, string webhookXml)
where TEvent : WechatOpenAIEvent
{
if (client is null) throw new ArgumentNullException(nameof(client));
if (webhookXml is null) throw new ArgumentNullException(webhookXml);
try
{
if (!Utilities.WxMsgCryptor.TryParseXml(webhookXml, out string? encryptedXml))
throw new WechatOpenAIException("Failed to decrypt event data, because of the encrypted data is empty.");
webhookXml = Utilities.WxMsgCryptor.AESDecrypt(cipherText: encryptedXml!, encodingAESKey: client.Credentials.EncodingAESKey!, out _);
return Utilities.XmlHelper.Deserialize<TEvent>(webhookXml);
}
catch (WechatOpenAIException)
{
throw;
}
catch (Exception ex)
{
throw new WechatOpenAIException("Failed to deserialize event data. Please see the inner exception for more details.", ex);
}
}
/// <summary>
/// <para>从 XML 反序列化得到 <see cref="WechatOpenAIEvent"/> 对象。</para>
/// </summary>
@@ -99,4 +75,31 @@ namespace SKIT.FlurlHttpClient.Wechat.OpenAI
return xml;
}
}
partial class WechatOpenAIClientEventExtensions
{
private static TEvent InnerDeserializeEventFromXml<TEvent>(this WechatOpenAIClient client, string webhookXml)
where TEvent : WechatOpenAIEvent
{
if (client is null) throw new ArgumentNullException(nameof(client));
if (webhookXml is null) throw new ArgumentNullException(webhookXml);
try
{
if (!Utilities.WxMsgCryptor.TryParseXml(webhookXml, out string? encryptedXml))
throw new WechatOpenAIException("Failed to decrypt event data, because of the encrypted data is empty.");
webhookXml = Utilities.WxMsgCryptor.AESDecrypt(cipherText: encryptedXml!, encodingAESKey: client.Credentials.EncodingAESKey!, out _);
return Utilities.XmlHelper.Deserialize<TEvent>(webhookXml);
}
catch (WechatOpenAIException)
{
throw;
}
catch (Exception ex)
{
throw new WechatOpenAIException("Failed to deserialize event data. Please see the inner exception for more details.", ex);
}
}
}
}