WebApi项目用户注册4位数字图形验证码实现及Redis存储方案咨询
4位纯数字图形验证码(WebApi+Redis)实现方案
一、图形验证码生成方案
目前主流后端栈都有成熟的图形绘制能力,核心逻辑通用,4位纯数字验证码的生成步骤如下:
- 生成范围在
0000~9999的4位随机数字串作为验证码内容 - 在带干扰线、噪点的画布上绘制验证码文字,避免被OCR工具批量识别
- 将绘制完成的画布输出为PNG格式的字节流,可直接转为Base64字符串返回给前端,不需要本地存储文件
以下是.NET栈的生成示例代码,Java/Go/Python等栈逻辑一致,仅调用的绘图API不同:
// 生成4位随机数字验证码 private string GenerateCaptchaCode() { Random random = new Random(Guid.NewGuid().GetHashCode()); return random.Next(1000, 9999).ToString(); } // 生成验证码图片字节流 private byte[] GenerateCaptchaImage(string code) { int width = 120; int height = 40; using var bitmap = new Bitmap(width, height); using var graphics = Graphics.FromImage(bitmap); // 填充浅灰色背景 graphics.Clear(Color.LightGray); Random random = new Random(); // 绘制10条随机干扰线 for (int i = 0; i < 10; i++) { int x1 = random.Next(width); int y1 = random.Next(height); int x2 = random.Next(width); int y2 = random.Next(height); graphics.DrawLine(new Pen(Color.Silver), x1, y1, x2, y2); } // 逐字绘制验证码,每个字用随机颜色 Font font = new Font("Arial", 20, FontStyle.Bold); for (int i = 0; i < code.Length; i++) { Brush brush = new SolidBrush(Color.FromArgb( random.Next(0, 150), random.Next(0, 150), random.Next(0, 150)) ); graphics.DrawString(code[i].ToString(), font, brush, i * 25 + 5, 2); } // 添加100个随机噪点 for (int i = 0; i < 100; i++) { bitmap.SetPixel(random.Next(width), random.Next(height), Color.FromArgb(random.Next(256))); } // 转为PNG字节流 using var ms = new MemoryStream(); bitmap.Save(ms, ImageFormat.Png); return ms.ToArray(); }
二、Redis存储对接逻辑
绑定验证码和用户请求的核心是通过唯一标识隔离不同用户的验证码,完整流程如下:
- 前端请求获取验证码接口
- 后端生成4位验证码code和全局唯一的
captchaId(推荐用GUID或雪花ID) - 以
captcha:{captchaId}为Redis键,验证码code为值,设置3~5分钟过期时间存入Redis - 接口将
captchaId和验证码图片的Base64字符串返回给前端 - 用户提交注册信息时,同时携带
captchaId和自己输入的验证码 - 后端根据
captchaId查询Redis,对比用户输入的验证码和存储的code是否一致 - 无论校验是否通过,都立即删除Redis中对应的键,防止同一个验证码被暴力多次尝试
- 校验通过继续走注册逻辑,校验失败返回验证码错误提示
以下是.NET栈使用分布式缓存操作Redis的示例代码:
// 注入.NET内置的分布式缓存,配置后默认对接Redis private readonly IDistributedCache _redisCache; // 存储验证码到Redis private async Task SaveCaptchaToRedis(string captchaId, string code) { var cacheOptions = new DistributedCacheEntryOptions { // 设置5分钟绝对过期 AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5) }; await _redisCache.SetStringAsync($"captcha:{captchaId}", code, cacheOptions); } // 校验验证码 private async Task<bool> VerifyCaptcha(string captchaId, string userInputCode) { var cacheKey = $"captcha:{captchaId}"; var savedCode = await _redisCache.GetStringAsync(cacheKey); // 验证码不存在直接返回错误 if (string.IsNullOrEmpty(savedCode)) return false; // 校验后立即删除,防止重复使用 await _redisCache.RemoveAsync(cacheKey); return savedCode.Equals(userInputCode, StringComparison.Ordinal); }
三、生产环境注意事项
- 验证码过期时间不要超过5分钟,降低被暴力破解的风险
- 高并发场景下可以提前批量生成验证码存入Redis,需要时直接取出返回,减少实时生成图片的CPU开销
- Redis键建议统一加
captcha:前缀,方便后续批量清理过期的验证码数据
内容的提问来源于stack exchange,提问作者user17483504
相关产品推荐
相关产品推荐

