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> /// </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);

View File

@@ -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 _);
} }

View File

@@ -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}\".");

View File

@@ -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();
} }
} }