Sparkdo.VerificationCode 3.0.0-preview.1

This is a prerelease version of Sparkdo.VerificationCode.
dotnet add package Sparkdo.VerificationCode --version 3.0.0-preview.1
                    
NuGet\Install-Package Sparkdo.VerificationCode -Version 3.0.0-preview.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Sparkdo.VerificationCode" Version="3.0.0-preview.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Sparkdo.VerificationCode" Version="3.0.0-preview.1" />
                    
Directory.Packages.props
<PackageReference Include="Sparkdo.VerificationCode" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Sparkdo.VerificationCode --version 3.0.0-preview.1
                    
#r "nuget: Sparkdo.VerificationCode, 3.0.0-preview.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Sparkdo.VerificationCode@3.0.0-preview.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Sparkdo.VerificationCode&version=3.0.0-preview.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Sparkdo.VerificationCode&version=3.0.0-preview.1&prerelease
                    
Install as a Cake Tool

Sparkdo.VerificationCode

验证码核心实现:CSPRNG 生成、版本化 PBKDF2 hash、cache/lock-backed manager、发送间隔限频, 一行 AddSparkdoVerificationCode() 接线。AOT 与裁剪安全。

快速开始

using Microsoft.Extensions.DependencyInjection;
using Sparkdo.VerificationCode;

var services = new ServiceCollection();

// 1) 本产品域:默认内存缓存、json 序列化、TTL 投影、默认服务
services.AddSparkdoVerificationCode(options =>
{
    options.Duration = TimeSpan.FromMinutes(5);
    options.SendInterval = TimeSpan.FromMinutes(1);
    options.HashSecret = secretsProvider.Get("VerificationCode:HashSecret"); // 可选 pepper
});

// 2) 分布式锁必须显式选择(本包故意不注册——见下文红线)
services.AddSparkdoDistributedLocking();   // 单节点:进程内锁
// 跨进程/跨机器部署:改接 Sparkdo.DistributedLocking.Redis

using var provider = services.BuildServiceProvider();
var manager = provider.GetRequiredService<IVerificationCodeManager>();

// 创建:明文只出现一次,直接交给发送 adapter
var info = await manager.CreateAsync(new VerificationCodeCreateOptions
{
    Key = "login:sms:user-42"
});
await smsSender.SendAsync(user.Phone, info.Code);

// 校验:成功即消费;失败计入失败次数,达到上限后验证码作废
var ok = await manager.VerifyAsync(info.Key, submittedCode);

为什么分布式锁要显式注册

同 key 串行化保证一次性消费与失败计数不丢更新。若本包静默注册进程内锁,多节点部署会 无声地失去这两条语义(fail-open);本包选择不注册,未接锁 provider 时解析 IVerificationCodeManager 立即失败(fail-closed),把部署形态的决定权留给宿主:

  • 单节点:AddSparkdoDistributedLocking()(进程内锁,零网络开销)。
  • 多节点:Sparkdo.DistributedLocking.Redis(Redis 原子脚本 provider)。

包内做了什么

服务 默认实现 注册语义
IVerificationCodeGenerator DefaultVerificationCodeGenerator(CSPRNG + 拒绝采样) TryAdd,可替换
IVerificationCodeHasher DefaultVerificationCodeHasher(PBKDF2-SHA256,版本化 payload) TryAdd,可替换
IVerificationCodeManager DefaultVerificationCodeManager(cache/lock-backed) TryAdd,可替换
IConfigureOptions<SparkdoCachingOptions> VerificationCodeCachingOptionsConfigure(TTL 投影) TryAddEnumerable,可组合
IJsonOptionsContributor 本包 cache payload 的 source-generated resolver TryAddEnumerable,可组合
IDistributedCache / ICache<> / IJsonSerializer 复用 AddSparkdoCaching / AddSparkdoJsonCore TryAdd,宿主实现优先

基础设施默认通过 TryAdd 组合:宿主先注册 Redis IDistributedCache 或自己的 IJsonSerializer 时不被覆盖;重复调用 AddSparkdoVerificationCode 幂等。

hash 设计

payload 格式 v1:{iterations}:{base64(hash32)},salt 为 16 字节 base64:

  • 版本化:算法或参数升级时换版本号,旧 payload 按自身字段校验,天然平滑迁移。
  • 迭代次数在 payload 内:调低全局 HashIterations 不使已发出的验证码失效; 校验按 payload 内的迭代次数执行(上限 10000000)。
  • pepper(可选):配置 HashSecret 后以 0x00 分隔拼接口令材料,泄漏缓存库 也无法离线穷举 6 位数字码;默认 null 表示不使用 pepper,此时只剩 600000 次迭代的 成本,10^6 个候选在 GPU 集群上可离线穷举——生产部署须由 secrets provider 提供 HashSecret。
  • 固定时间比较:CryptographicOperations.FixedTimeEquals,无短路时序侧信道。
  • fail-closed:payload 版本、迭代次数、base64、salt/hash 长度任一非法一律返回 false(且拒绝尾部垃圾字符),不抛异常。

性能与分配(实测,.NET 11 RC / ShortRun 基准)

路径 耗时 托管分配
生成 6 位数字码 ~0.7 µs 40 B(仅结果字符串)
Hash(1k 迭代) ~0.33 ms ~234 B(2 个结果字符串 + BCL Pbkdf2 内部开销)
Verify(1k 迭代) ~0.33 ms ≤ 34 B
Verify(600k 迭代,默认) ~193 ms 0 B 显示精度
Verify(非法 payload 快速失败) ~3 ns 0 B

Verify 全链路 span 化:payload 解析(span 切片 + Base64.DecodeFromChars)、 salt/hash/派生结果(栈上)、大小写归一与 UTF-8 编码(免分配,超预算走 ArrayPool)——本实现自身零堆分配;唯一分配来自 BCL Rfc2898DeriveBytes.Pbkdf2 单次调用的内部开销(CNG 提供者句柄),手写 PBKDF2 以换取消除它不值得。

默认 600000 次迭代意味着每次 Create/Verify 约 200 ms CPU——这是抬高「缓存库被拖库 后离线穷举低熵码」成本的定价,但只有同时配置 HashSecret(pepper)时才对 6 位码 构成实际阻碍。吞吐敏感场景可调低 HashIterations(payload 版本化保证平滑过渡), 或评估 peppered 快速 hash(HMAC)方案的安全取舍。

存储与 TTL

  • 验证码 payload(VerificationCodeCacheItem):hash、salt、ExpireAt、失败计数、 大小写设置,经 ICache<> 走 Sparkdo.Caching(Redis provider 开箱即用)。
  • 发送间隔 payload(VerificationCodeIntervalCacheItem):间隔到期时间。
  • 物理 TTL 由 VerificationCodeCachingOptionsConfigure 从全局 Options 投影: 验证码 TTL = Duration、间隔 TTL = SendInterval;逻辑过期由 manager 读取时 再校验,物理 TTL 只是清理兜底。
  • 失败计数回写把物理 TTL 钉在条目剩余寿命上;剩余时间不足 1 毫秒(tick 级或因 校验耗时转负)时以 1 毫秒兜底,保证物理清理窗口在底层 provider 侧始终有效—— 逻辑过期仍是唯一权威,可校验寿命不会被拉长。

常见坑

  • 多节点没接 Redis 锁:进程内锁只在本进程串行化;跨机器部署必须接 Redis provider, 否则一次性消费与失败计数在节点间各自为政。
  • HashSecret 换值:换 pepper 后旧验证码全部校验失败(hash 不匹配)—— peppering 与盐不同,不支持就地轮换;灰度期请用新 key 前缀过渡。
  • 调低 HashIterations 不影响存量验证码:校验按 payload 内的迭代次数执行; 想强制全部重发就清缓存。
  • 明文进日志:VerificationCodeInfo.Code 只应交给发送 adapter;打了日志等于 放弃「服务端不存明文」的审计承诺。
  • 单次配置超过全局上限会抛 InvalidOptions:单次 Duration/SendInterval 是「上限内的收紧」,不是「任意放大」。
  • 把用户可控值透传给 Length:长度超过 VerificationCodeOptions.MaxLength(64) 会在 manager 校验与 VerificationCodeGenerationContext 构造期被显式拒绝 (InvalidOptions / ArgumentOutOfRangeException)——生成路径按长度分配, 别把没有上限的输入直接塞进去。
  • 字符集重复字符:会在 VerificationCodeGenerationContext 构造期抛异常—— 想加字符请确认唯一性,这是熵守门,不是苛刻。
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed.  net11.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Sparkdo.VerificationCode:

Package Downloads
Sparkdo.VerificationCode.Imaging

Sparkdo 验证码图像库,提供验证码图像生成功能实现

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.0.0-preview.1 58 10/30/2026
2.0.0-preview.2 89 7/20/2026
2.0.0-preview.1 81 7/18/2026
1.0.4-preview.3 87 6/8/2026
1.0.4-preview.2 84 6/7/2026
1.0.4-preview.1 96 6/7/2026
1.0.3 179 6/6/2026
1.0.3-preview.1 89 5/17/2026
1.0.2 152 4/27/2026
1.0.2-preview.5 92 4/15/2026
1.0.2-preview.4 121 2/8/2026
1.0.2-preview.3 117 2/1/2026
1.0.2-preview.2 103 1/31/2026
1.0.2-preview.1 200 12/4/2025