如何在搭载IdentityServer的ASP.NET Core 5.0应用中实现验证码功能
基于ASP.NET Core Identity + IdentityServer4的验证码实现方案
1. 前置准备
验证码一般需要添加在注册、登录、密码重置等敏感用户操作节点,正式开发前需要先完成以下准备:
- 用Identity脚手架导出默认UI页面,方便自定义表单和处理逻辑,脚手架命令为:
dotnet aspnet-codegenerator identity -dc 你的项目DbContext名称 - 准备分布式缓存组件(如Redis)存储验证码状态,多实例部署场景禁止使用内存缓存,会导致跨实例验证码校验失败
- 按需选择实现方案:自定义图形Captcha(完全可控、无外部依赖)或者Google reCAPTCHA(开发量小、防爬虫效果更好)
2. 自定义图形Captcha实现步骤
2.1 安装依赖包
推荐使用SixLabors.ImageSharp做图形绘制,执行命令安装:dotnet add package SixLabors.ImageSharp
2.2 封装验证码服务
新增ICaptchaService接口和对应实现类,核心逻辑包含生成和校验两个方法:
// 验证码生成方法 public async Task<(string CaptchaId, byte[] ImageData)> GenerateAsync(int length = 4, int width = 120, int height = 40) { // 生成4位随机验证码(可自定义规则,比如纯数字、字母+数字组合) var captchaCode = new Random().Next(1000, 9999).ToString(); var captchaId = Guid.NewGuid().ToString("N"); // 验证码存入分布式缓存,有效期5分钟 await _distributedCache.SetStringAsync($"captcha:{captchaId}", captchaCode, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5) }); // 用ImageSharp绘制带噪点、干扰线的验证码图片,返回PNG字节流 using var image = new Image<Rgba32>(width, height); // 省略图片绘制逻辑(填充背景、写验证码文字、加干扰元素) var imageBytes = image.SaveAsPngBytes(); return (captchaId, imageBytes); } // 验证码校验方法 public async Task<bool> ValidateAsync(string captchaId, string inputCode) { var cacheKey = $"captcha:{captchaId}"; var storedCode = await _distributedCache.GetStringAsync(cacheKey); if (storedCode == null || !storedCode.Equals(inputCode, StringComparison.OrdinalIgnoreCase)) return false; // *校验通过立即删除缓存,防止同一个验证码被重复恶意使用* await _distributedCache.RemoveAsync(cacheKey); return true; }
在Program.cs中注册服务:builder.Services.AddScoped<ICaptchaService, CaptchaService>();
2.3 新增验证码获取接口
新增Controller提供验证码图片访问入口:
[HttpGet] [AllowAnonymous] public async Task<IActionResult> GetCaptcha() { var (captchaId, imageData) = await _captchaService.GenerateAsync(); // 验证码ID存入HttpOnly Cookie,防止前端篡改 Response.Cookies.Append("captcha_id", captchaId, new CookieOptions { HttpOnly = true, Secure = true, SameSite = SameSiteMode.Strict, Expires = DateTimeOffset.UtcNow.AddMinutes(5) }); return File(imageData, "image/png"); }
2.4 修改Identity页面表单和处理逻辑
- 在注册/登录页面的表单中添加验证码输入框和图片展示:
<div class="form-group"> <label>请输入验证码</label> <div class="d-flex align-items-center gap-2"> <input asp-for="Input.Captcha" class="form-control" autocomplete="off" /> <img src="/Account/GetCaptcha" id="captchaImg" style="cursor:pointer;height:38px" alt="验证码" title="点击刷新"> </div> <span asp-validation-for="Input.Captcha" class="text-danger"></span> </div> <script> // 点击刷新验证码 document.getElementById('captchaImg').addEventListener('click', function() { this.src = '/Account/GetCaptcha?t=' + new Date().getTime(); }); </script>
- 在对应PageModel的Post处理逻辑开头添加校验:
public async Task<IActionResult> OnPostAsync(string returnUrl = null) { var captchaId = Request.Cookies["captcha_id"]; var isValid = await _captchaService.ValidateAsync(captchaId, Input.Captcha); if (!isValid) { ModelState.AddModelError(string.Empty, "验证码错误或已过期"); return Page(); } // 原有Identity注册/登录逻辑保留 }
3. reCAPTCHA实现步骤
如果不想自己维护图形验证码逻辑,可以直接接入reCAPTCHA:
- 申请reCAPTCHA的站点密钥(SiteKey)和服务端密钥(SecretKey),存入
appsettings.json - 前端表单中引入reCAPTCHA组件,提交时会自动生成
g-recaptcha-response参数 - 服务端在Post处理逻辑中拿到该参数,向reCAPTCHA校验接口发起请求验证即可,reCAPTCHA v3不需要用户手动点击,会自动根据用户行为打分,可根据业务场景调整校验阈值
4. IdentityServer4兼容注意事项
- 若要修改IdentityServer4的授权页面(登录、同意授权页),修改逻辑和上述Identity页面完全一致,直接导出IdentityServer4的UI源码后添加校验即可
- 不要将验证码校验逻辑加入Identity的密码验证器中,避免影响ClientCredentials等无需用户交互的OAuth授权模式
- 若需要给受IdentityServer4保护的业务接口加验证码,直接在对应接口层添加校验逻辑即可,无需修改IdentityServer4核心配置
内容的提问来源于stack exchange,提问作者Boppity Bop
相关产品推荐
相关产品推荐

