feat: 增加服务号回调签名校验及扫码回调幂等处理

- `FuwuhaoManager` 新增 `ValidateCallback` 方法,用于校验微信回调签名
- `FuwuhaoOptions` 增加 `CallbackToken` 配置项
- `QrCodeResponse` 属性添加 `JsonPropertyName` 标注,支持 JSON 序列化映射
- `FuwuhaoService` 在回调接口中增加签名校验,并通过分布式锁实现幂等处理
- 调整场景值解析逻辑,过滤非扫码/关注事件
- 优化缓存过期时间设置
This commit is contained in:
chenchun
2025-08-28 15:20:15 +08:00
parent b768bca638
commit 1d108983e8
5 changed files with 78 additions and 17 deletions

View File

@@ -1,4 +1,5 @@
using System.Text;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
@@ -18,11 +19,12 @@ public class FuwuhaoManager : DomainService
private ISqlSugarRepository<AiUserExtraInfoEntity> _userRepository;
public FuwuhaoManager(IOptions<FuwuhaoOptions> options, IHttpClientFactory httpClientFactory,
ISqlSugarRepository<AiUserExtraInfoEntity> userRepository)
ISqlSugarRepository<AiUserExtraInfoEntity> userRepository, IDistributedCache<AccessTokenResponse> accessTokenCache)
{
_options = options.Value;
_httpClientFactory = httpClientFactory;
_userRepository = userRepository;
_accessTokenCache = accessTokenCache;
}
/// <summary>
@@ -66,11 +68,11 @@ public class FuwuhaoManager : DomainService
var requestBody = new
{
action_name = "QR_STR_SCENE",
expire_seconds = 600,
action_info = new
{
scene = new
{
expire_seconds = 600,
scene_str = scene
}
}
@@ -130,6 +132,40 @@ public class FuwuhaoManager : DomainService
return result;
}
/// <summary>
/// 校验微信服务器回调参数是否正确
/// </summary>
/// <param name="signature">微信加密签名</param>
/// <param name="timestamp">时间戳</param>
/// <param name="nonce">随机数</param>
/// <returns>true表示验证通过false表示验证失败</returns>
public void ValidateCallback(string signature, string timestamp, string nonce)
{
var token = _options.CallbackToken; // 您设置的token
// 将token、timestamp、nonce三个参数进行字典序排序
var parameters = new[] { token, timestamp, nonce };
Array.Sort(parameters);
// 将三个参数字符串拼接成一个字符串
var concatenated = string.Join("", parameters);
// 进行SHA1计算签名
using (var sha1 = SHA1.Create())
{
var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(concatenated));
var calculatedSignature = BitConverter.ToString(hash).Replace("-", "").ToLower();
// 与URL链接中的signature参数进行对比
var result = calculatedSignature.Equals(signature, StringComparison.OrdinalIgnoreCase);
if (!result)
{
throw new UserFriendlyException("服务号回调签名异常");
}
}
}
/// <summary>
/// 处理回调逻辑
/// </summary>

View File

@@ -11,4 +11,9 @@ public class FuwuhaoOptions
/// 微信公众号AppSecret
/// </summary>
public string Secret { get; set; }
/// <summary>
/// 回调token
/// </summary>
public string CallbackToken { get; set; }
}

View File

@@ -1,3 +1,5 @@
using System.Text.Json.Serialization;
namespace Yi.Framework.AiHub.Domain.Managers.Fuwuhao;
/// <summary>
@@ -8,15 +10,18 @@ public class QrCodeResponse
/// <summary>
/// 二维码票据
/// </summary>
[JsonPropertyName("ticket")]
public string Ticket { get; set; }
/// <summary>
/// 过期时间(秒)
/// </summary>
[JsonPropertyName("expire_seconds")]
public int ExpireSeconds { get; set; }
/// <summary>
/// 二维码URL
/// </summary>
[JsonPropertyName("url")]
public string Url { get; set; }
}