mirror of
https://gitee.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat.git
synced 2025-09-19 18:22:24 +08:00
feat(work): 异步的消息加解密密钥管理器
This commit is contained in:
@@ -17,6 +17,11 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.Settings
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string PrivateKey { get; }
|
public string PrivateKey { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="version"></param>
|
||||||
|
/// <param name="privateKey"></param>
|
||||||
[Newtonsoft.Json.JsonConstructor]
|
[Newtonsoft.Json.JsonConstructor]
|
||||||
[System.Text.Json.Serialization.JsonConstructor]
|
[System.Text.Json.Serialization.JsonConstructor]
|
||||||
public EncryptionKeyEntry(int version, string privateKey)
|
public EncryptionKeyEntry(int version, string privateKey)
|
||||||
@@ -32,12 +37,14 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.Settings
|
|||||||
PrivateKey = privateKey;
|
PrivateKey = privateKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public bool Equals(EncryptionKeyEntry other)
|
public bool Equals(EncryptionKeyEntry other)
|
||||||
{
|
{
|
||||||
return int.Equals(Version, other.Version) &&
|
return int.Equals(Version, other.Version) &&
|
||||||
string.Equals(PrivateKey, other.PrivateKey);
|
string.Equals(PrivateKey, other.PrivateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(null, obj))
|
if (ReferenceEquals(null, obj))
|
||||||
@@ -48,20 +55,23 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.Settings
|
|||||||
return Equals((EncryptionKeyEntry)obj);
|
return Equals((EncryptionKeyEntry)obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
#if NETFRAMEWORK || NETSTANDARD2_0
|
#if NETCOREAPP || NET5_0_OR_GREATER
|
||||||
return (Version.GetHashCode(), PrivateKey?.GetHashCode()).GetHashCode();
|
|
||||||
#else
|
|
||||||
return HashCode.Combine(Version.GetHashCode(), PrivateKey?.GetHashCode());
|
return HashCode.Combine(Version.GetHashCode(), PrivateKey?.GetHashCode());
|
||||||
|
#else
|
||||||
|
return (Version.GetHashCode(), PrivateKey?.GetHashCode()).GetHashCode();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public static bool operator ==(EncryptionKeyEntry left, EncryptionKeyEntry right)
|
public static bool operator ==(EncryptionKeyEntry left, EncryptionKeyEntry right)
|
||||||
{
|
{
|
||||||
return left.Equals(right);
|
return left.Equals(right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public static bool operator !=(EncryptionKeyEntry left, EncryptionKeyEntry right)
|
public static bool operator !=(EncryptionKeyEntry left, EncryptionKeyEntry right)
|
||||||
{
|
{
|
||||||
return !left.Equals(right);
|
return !left.Equals(right);
|
||||||
|
@@ -1,45 +1,70 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.Settings
|
namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.Settings
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 企业微信会话内容存档的消息加解密密钥管理器接口。
|
/// 企业微信会话内容存档的消息加解密密钥管理器接口。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class EncryptionKeyManager
|
public interface IEncryptionKeyManager
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 获取存储的全部消息加解密密钥实体。
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public abstract IEnumerable<EncryptionKeyEntry> AllEntries();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 添加一个消息加解密密钥实体。
|
/// 添加一个消息加解密密钥实体。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="entry"></param>
|
/// <param name="entry"></param>
|
||||||
public abstract void AddEntry(EncryptionKeyEntry entry);
|
void AddEntry(EncryptionKeyEntry entry);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 根据版本号获取消息加解密密钥实体。
|
/// 根据版本号获取消息加解密密钥实体。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="version"></param>
|
/// <param name="version"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public abstract EncryptionKeyEntry? GetEntry(int version);
|
EncryptionKeyEntry? GetEntry(int version);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 根据版本号移除消息加解密密钥实体。
|
/// 根据版本号移除消息加解密密钥实体。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="version"></param>
|
/// <param name="version"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public abstract bool RemoveEntry(int version);
|
bool RemoveEntry(int version);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 一个基于内存实现的 <see cref="EncryptionKeyManager"/>。
|
/// 企业微信会话内容存档的消息加解密密钥管理器接口。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class InMemoryEncryptionKeyManager : EncryptionKeyManager
|
public interface IEncryptionKeyManagerAsync : IEncryptionKeyManager
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 异步添加一个消息加解密密钥实体。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entry"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
Task AddEntryAsync(EncryptionKeyEntry entry, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步根据版本号获取消息加解密密钥实体。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="version"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<EncryptionKeyEntry?> GetEntryAsync(int version, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步根据版本号移除消息加解密密钥实体。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="version"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<bool> RemoveEntryAsync(int version, CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 一个基于内存实现的 <see cref="IEncryptionKeyManager"/>。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class InMemoryEncryptionKeyManager : IEncryptionKeyManager
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<int, EncryptionKeyEntry> _dict;
|
private readonly ConcurrentDictionary<int, EncryptionKeyEntry> _dict;
|
||||||
|
|
||||||
@@ -48,20 +73,14 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.Settings
|
|||||||
_dict = new ConcurrentDictionary<int, EncryptionKeyEntry>();
|
_dict = new ConcurrentDictionary<int, EncryptionKeyEntry>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<EncryptionKeyEntry> AllEntries()
|
public void AddEntry(EncryptionKeyEntry entry)
|
||||||
{
|
{
|
||||||
return _dict.Values.ToArray();
|
_dict.AddOrUpdate(entry.Version, (_) => entry, (_, _) => entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void AddEntry(EncryptionKeyEntry entry)
|
public EncryptionKeyEntry? GetEntry(int version)
|
||||||
{
|
{
|
||||||
_dict.TryRemove(entry.Version, out _);
|
if (_dict.TryGetValue(version, out EncryptionKeyEntry entry))
|
||||||
_dict.TryAdd(entry.Version, entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override EncryptionKeyEntry? GetEntry(int version)
|
|
||||||
{
|
|
||||||
if (_dict.TryGetValue(version, out var entry))
|
|
||||||
{
|
{
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
@@ -69,7 +88,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.Settings
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool RemoveEntry(int version)
|
public bool RemoveEntry(int version)
|
||||||
{
|
{
|
||||||
return _dict.TryRemove(version, out _);
|
return _dict.TryRemove(version, out _);
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance
|
|||||||
{
|
{
|
||||||
using SKIT.FlurlHttpClient.Primitives;
|
using SKIT.FlurlHttpClient.Primitives;
|
||||||
using SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.InteropServices;
|
using SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.InteropServices;
|
||||||
|
using SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.Settings;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 一个企业微信会话内容存档 API HTTP 客户端。
|
/// 一个企业微信会话内容存档 API HTTP 客户端。
|
||||||
@@ -28,12 +29,12 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取当前客户端使用的企业微信会话内容存档凭证。
|
/// 获取当前客户端使用的企业微信会话内容存档凭证。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Settings.Credentials Credentials { get; }
|
public Credentials Credentials { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取当前客户端使用的企业微信会话内容存档消息加解密密钥管理器。
|
/// 获取当前客户端使用的企业微信会话内容存档消息加解密密钥管理器。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Settings.EncryptionKeyManager EncryptionKeyManager { get; }
|
public IEncryptionKeyManager EncryptionKeyManager { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用指定的配置项初始化 <see cref="WechatWorkFinanceClient"/> 类的新实例。
|
/// 用指定的配置项初始化 <see cref="WechatWorkFinanceClient"/> 类的新实例。
|
||||||
@@ -44,7 +45,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance
|
|||||||
{
|
{
|
||||||
if (options is null) throw new ArgumentNullException(nameof(options));
|
if (options is null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
Credentials = new Settings.Credentials(options);
|
Credentials = new Credentials(options);
|
||||||
EncryptionKeyManager = options.EncryptionKeyManager;
|
EncryptionKeyManager = options.EncryptionKeyManager;
|
||||||
|
|
||||||
_timeout = options.Timeout;
|
_timeout = options.Timeout;
|
||||||
@@ -190,13 +191,15 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance
|
|||||||
|
|
||||||
EnsureInitialized();
|
EnsureInitialized();
|
||||||
|
|
||||||
return Task.Run(() =>
|
return Task.Run(async () =>
|
||||||
{
|
{
|
||||||
string encryptKey;
|
string encryptKey;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Settings.EncryptionKeyEntry? encryptionKeyEntry = EncryptionKeyManager.GetEntry(request.PublicKeyVersion);
|
EncryptionKeyEntry? encryptionKeyEntry = (EncryptionKeyManager is IEncryptionKeyManagerAsync asyncMananger)
|
||||||
|
? await asyncMananger.GetEntryAsync(request.PublicKeyVersion, cancellationToken).ConfigureAwait(false)
|
||||||
|
: EncryptionKeyManager.GetEntry(request.PublicKeyVersion);
|
||||||
if (!encryptionKeyEntry.HasValue)
|
if (!encryptionKeyEntry.HasValue)
|
||||||
throw new WechatWorkFinanceException($"Failed to decrypt random key of the encrypted chat data, because there is no private key matched the verion: \"{request.PublicKeyVersion}\".");
|
throw new WechatWorkFinanceException($"Failed to decrypt random key of the encrypted chat data, because there is no private key matched the verion: \"{request.PublicKeyVersion}\".");
|
||||||
|
|
||||||
|
@@ -35,6 +35,6 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance
|
|||||||
/// 获取或设置企业微信会话内容存档消息加解密密钥管理器。
|
/// 获取或设置企业微信会话内容存档消息加解密密钥管理器。
|
||||||
/// <para>默认值:<see cref="Settings.InMemoryEncryptionKeyManager"/></para>
|
/// <para>默认值:<see cref="Settings.InMemoryEncryptionKeyManager"/></para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Settings.EncryptionKeyManager EncryptionKeyManager { get; set; } = new Settings.InMemoryEncryptionKeyManager();
|
public Settings.IEncryptionKeyManager EncryptionKeyManager { get; set; } = new Settings.InMemoryEncryptionKeyManager();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user