feat(work): 异步的消息加解密密钥管理器

This commit is contained in:
Fu Diwei 2024-02-06 12:48:09 +08:00 committed by RHQYZ
parent 07d65882ff
commit a536d5049c
4 changed files with 64 additions and 32 deletions

View File

@ -17,6 +17,11 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.Settings
/// </summary>
public string PrivateKey { get; }
/// <summary>
///
/// </summary>
/// <param name="version"></param>
/// <param name="privateKey"></param>
[Newtonsoft.Json.JsonConstructor]
[System.Text.Json.Serialization.JsonConstructor]
public EncryptionKeyEntry(int version, string privateKey)
@ -32,12 +37,14 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.Settings
PrivateKey = privateKey;
}
/// <inheritdoc/>
public bool Equals(EncryptionKeyEntry other)
{
return int.Equals(Version, other.Version) &&
string.Equals(PrivateKey, other.PrivateKey);
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
@ -48,20 +55,23 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.Settings
return Equals((EncryptionKeyEntry)obj);
}
/// <inheritdoc/>
public override int GetHashCode()
{
#if NETFRAMEWORK || NETSTANDARD2_0
return (Version.GetHashCode(), PrivateKey?.GetHashCode()).GetHashCode();
#else
#if NETCOREAPP || NET5_0_OR_GREATER
return HashCode.Combine(Version.GetHashCode(), PrivateKey?.GetHashCode());
#else
return (Version.GetHashCode(), PrivateKey?.GetHashCode()).GetHashCode();
#endif
}
/// <inheritdoc/>
public static bool operator ==(EncryptionKeyEntry left, EncryptionKeyEntry right)
{
return left.Equals(right);
}
/// <inheritdoc/>
public static bool operator !=(EncryptionKeyEntry left, EncryptionKeyEntry right)
{
return !left.Equals(right);

View File

@ -1,45 +1,70 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.Settings
{
/// <summary>
/// 企业微信会话内容存档的消息加解密密钥管理器接口。
/// </summary>
public abstract class EncryptionKeyManager
public interface IEncryptionKeyManager
{
/// <summary>
/// 获取存储的全部消息加解密密钥实体。
/// </summary>
/// <returns></returns>
public abstract IEnumerable<EncryptionKeyEntry> AllEntries();
/// <summary>
/// 添加一个消息加解密密钥实体。
/// </summary>
/// <param name="entry"></param>
public abstract void AddEntry(EncryptionKeyEntry entry);
void AddEntry(EncryptionKeyEntry entry);
/// <summary>
/// 根据版本号获取消息加解密密钥实体。
/// </summary>
/// <param name="version"></param>
/// <returns></returns>
public abstract EncryptionKeyEntry? GetEntry(int version);
EncryptionKeyEntry? GetEntry(int version);
/// <summary>
/// 根据版本号移除消息加解密密钥实体。
/// </summary>
/// <param name="version"></param>
/// <returns></returns>
public abstract bool RemoveEntry(int version);
bool RemoveEntry(int version);
}
/// <summary>
/// 一个基于内存实现的 <see cref="EncryptionKeyManager"/>
/// 企业微信会话内容存档的消息加解密密钥管理器接口
/// </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;
@ -48,20 +73,14 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.Settings
_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 _);
_dict.TryAdd(entry.Version, entry);
}
public override EncryptionKeyEntry? GetEntry(int version)
{
if (_dict.TryGetValue(version, out var entry))
if (_dict.TryGetValue(version, out EncryptionKeyEntry entry))
{
return entry;
}
@ -69,7 +88,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.Settings
return null;
}
public override bool RemoveEntry(int version)
public bool RemoveEntry(int version)
{
return _dict.TryRemove(version, out _);
}

View File

@ -9,6 +9,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance
{
using SKIT.FlurlHttpClient.Primitives;
using SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.InteropServices;
using SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance.Settings;
/// <summary>
/// 一个企业微信会话内容存档 API HTTP 客户端。
@ -28,12 +29,12 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance
/// <summary>
/// 获取当前客户端使用的企业微信会话内容存档凭证。
/// </summary>
public Settings.Credentials Credentials { get; }
public Credentials Credentials { get; }
/// <summary>
/// 获取当前客户端使用的企业微信会话内容存档消息加解密密钥管理器。
/// </summary>
public Settings.EncryptionKeyManager EncryptionKeyManager { get; }
public IEncryptionKeyManager EncryptionKeyManager { get; }
/// <summary>
/// 用指定的配置项初始化 <see cref="WechatWorkFinanceClient"/> 类的新实例。
@ -44,7 +45,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance
{
if (options is null) throw new ArgumentNullException(nameof(options));
Credentials = new Settings.Credentials(options);
Credentials = new Credentials(options);
EncryptionKeyManager = options.EncryptionKeyManager;
_timeout = options.Timeout;
@ -190,13 +191,15 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance
EnsureInitialized();
return Task.Run(() =>
return Task.Run(async () =>
{
string encryptKey;
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)
throw new WechatWorkFinanceException($"Failed to decrypt random key of the encrypted chat data, because there is no private key matched the verion: \"{request.PublicKeyVersion}\".");

View File

@ -35,6 +35,6 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.ExtendedSDK.Finance
/// 获取或设置企业微信会话内容存档消息加解密密钥管理器。
/// <para>默认值:<see cref="Settings.InMemoryEncryptionKeyManager"/></para>
/// </summary>
public Settings.EncryptionKeyManager EncryptionKeyManager { get; set; } = new Settings.InMemoryEncryptionKeyManager();
public Settings.IEncryptionKeyManager EncryptionKeyManager { get; set; } = new Settings.InMemoryEncryptionKeyManager();
}
}