无私分享:ASP.NET CORE 项目实战(第十四章)图形验证码的实现
在现代 Web 应用中,图形验证码是防止自动化攻击(如暴力破解、批量注册等)的有效手段。本文将通过 ASP.NET Core 实现一个高度安全的图形验证码系统,包含随机字符生成、干扰元素绘制、缓存存储及校验逻辑。我们将使用纯代码生成验证码图片,避免第三方依赖,保证系统的安全性和灵活性。
目录#
- 图形验证码基础
- 为什么需要图形验证码?
- 常见实现方式对比
- ASP.NET Core 实现方案
- 项目结构概述
- 核心类设计
- 验证码生成技术细节
- 随机字符串生成算法
- 使用
System.Drawing绘制图片 - 添加干扰元素
- 验证码的存储与校验
- 使用内存缓存存储验证码
- 校验逻辑实现
- 集成到 MVC 系统
- 控制器实现
- Razor视图集成
- 最佳实践与安全性
- 防破解设计
- 性能优化建议
- 示例用法
- 登录页面集成
- AJAX验证实现
- 完整代码参考
- 参考资源
1. 图形验证码基础#
为什么需要图形验证码?#
图形验证码主要用于:
- 防止自动化脚本攻击
- 降低暴力破解密码的成功率
- 防止垃圾注册和垃圾评论
- 区分人类用户和机器人程序
常见实现方式对比#
| 实现方式 | 优点 | 缺点 |
|---|---|---|
| 第三方API | 实现简单,功能丰富 | 依赖外部服务,可能存在费用 |
| 纯代码生成 | 完全可控,无额外依赖 | 实现复杂度较高 |
| JavaScript 库 | 前端生成,减轻服务器压力 | 安全性较低,易被绕过 |
推荐选择:ASP.NET Core 服务端生成,保证安全性与可控性。
2. ASP.NET Core 实现方案#
项目结构#
/Controllers
/AccountController.cs
/Services
/CaptchaService.cs
/Views
/Account
/Login.cshtml
核心类设计#
public interface ICaptchaService
{
byte[] GenerateCaptcha(out string captchaText);
bool ValidateCaptcha(string userInput);
}3. 验证码生成技术细节#
步骤 1:安装必要 NuGet 包#
Install-Package System.Drawing.Common
Install-Package Microsoft.Extensions.Caching.Memory步骤 2:生成随机字符串#
private string GenerateRandomText(int length = 4)
{
// 排除易混淆字符:0O1Il
const string validChars = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
var random = new Random();
return new string(Enumerable.Repeat(validChars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}步骤 3:使用 System.Drawing 绘制图片#
public byte[] GenerateCaptcha(out string captchaText)
{
captchaText = GenerateRandomText();
using var bitmap = new Bitmap(120, 40);
using var graphics = Graphics.FromImage(bitmap);
// 设置背景色
graphics.Clear(Color.White);
// 绘制文字
using var font = new Font("Arial", 18, FontStyle.Bold);
using var brush = new SolidBrush(Color.Black);
graphics.DrawString(captchaText, font, brush, 10, 10);
// 添加干扰线
var random = new Random();
for (int i = 0; i < 5; i++)
{
using var pen = new Pen(GetRandomColor(random));
graphics.DrawLine(pen,
random.Next(0, 120), random.Next(0, 40),
random.Next(0, 120), random.Next(0, 40));
}
// 添加噪点
for (int i = 0; i < 500; i++)
{
bitmap.SetPixel(random.Next(0, 120), random.Next(0, 40), GetRandomColor(random));
}
// 保存到内存流
using var stream = new MemoryStream();
bitmap.Save(stream, ImageFormat.Png);
return stream.ToArray();
}步骤 4:高级干扰技术#
// 波纹扭曲效果(增强防OCR能力)
private void ApplyWaveDistortion(Bitmap bitmap)
{
var clone = (Bitmap)bitmap.Clone();
var rnd = new Random();
for (int y = 0; y < clone.Height; y++)
{
for (int x = 0; x < clone.Width; x++)
{
var newX = (int)(x + (Math.Sin(y * 0.05) * 2));
var newY = (int)(y + (Math.Cos(x * 0.05) * 2));
newX = Math.Clamp(newX, 0, clone.Width - 1);
newY = Math.Clamp(newY, 0, clone.Height - 1);
bitmap.SetPixel(x, y, clone.GetPixel(newX, newY));
}
}
}4. 验证码的存储与校验#
使用内存缓存存储#
public class CaptchaService : ICaptchaService
{
private readonly IMemoryCache _cache;
private const string CachePrefix = "Captcha_";
public CaptchaService(IMemoryCache memoryCache)
{
_cache = memoryCache;
}
public byte[] GenerateCaptcha(out string captchaText)
{
captchaText = GenerateRandomText();
var cacheKey = $"{CachePrefix}{Guid.NewGuid()}";
// 缓存5分钟
_cache.Set(cacheKey, captchaText,
new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(5)));
return GenerateImage(captchaText);
}
public bool ValidateCaptcha(string cacheKey, string userInput)
{
if (string.IsNullOrEmpty(cacheKey) ||
string.IsNullOrEmpty(userInput))
return false;
if (_cache.TryGetValue($"{CachePrefix}{cacheKey}", out string validText))
{
// 立即清除已使用的验证码
_cache.Remove(cacheKey);
return string.Equals(
userInput, validText,
StringComparison.OrdinalIgnoreCase); // 忽略大小写
}
return false;
}
}5. 集成到 MVC 系统#
控制器实现#
public class AccountController : Controller
{
private readonly ICaptchaService _captchaService;
public AccountController(ICaptchaService captchaService)
{
_captchaService = captchaService;
}
[HttpGet("captcha")]
public IActionResult GetCaptcha()
{
var imageBytes = _captchaService.GenerateCaptcha(out string captchaText);
// 将验证码标识符存储在TempData中(或通过其他方式传递到前端)
HttpContext.Response.Cookies.Append(
"captcha_key",
Guid.NewGuid().ToString(),
new CookieOptions { HttpOnly = true });
return File(imageBytes, "image/png");
}
[HttpPost("login")]
public IActionResult Login(LoginModel model)
{
// 获取前端传递的标识符
var captchaKey = Request.Cookies["captcha_key"];
if (!_captchaService.ValidateCaptcha(captchaKey, model.Captcha))
{
ModelState.AddModelError("Captcha", "验证码错误");
return View(model);
}
// ... 其他登录逻辑
}
}Razor视图集成#
<!-- Login.cshtml -->
<form asp-action="Login">
<div class="form-group">
<label>验证码</label>
<div class="input-group">
<input type="text" class="form-control" name="Captcha" />
<div class="input-group-append">
<img id="captchaImg" src="/captcha"
alt="点击刷新验证码"
style="cursor:pointer;height:40px;" />
</div>
</div>
<input type="hidden" id="captchaKey" name="CaptchaKey" />
</div>
<button type="submit">登录</button>
</form>
@section Scripts{
<script>
// 点击刷新验证码
$("#captchaImg").click(function() {
$(this).attr("src", "/captcha?t=" + new Date().getTime());
});
// 初始化时获取验证码标识符
document.cookie.split(';').forEach(cookie => {
const [name, value] = cookie.trim().split('=');
if(name === "captcha_key") {
$("#captchaKey").val(value);
}
});
</script>
}6. 最佳实践与安全性#
防破解设计#
- 动态干扰元素:每次生成的干扰线、噪点位置随机
- 字符变形:添加旋转和扭曲效果
- 字符集优化:排除0/O、1/I等易混淆字符
- 长度控制:推荐4-6个字符,平衡用户体验与安全性
- 大小写处理:统一转换为大写或小写进行验证
性能优化#
// 单例模式重用对象池
private readonly ObjectPool<Graphics> _graphicsPool =
ObjectPool.Create<Graphics>();
// 在生成图片方法中使用对象池
using (var graphics = _graphicsPool.Get())
{
// 使用graphics对象操作
_graphicsPool.Return(graphics);
}缓存策略建议#
| 策略 | 适用场景 |
|---|---|
| 内存缓存 (IMemoryCache) | 单服务器部署 |
| IDistributedCache | 多服务器/集群部署 |
| Session存储 | 对安全性要求极高的场景 |
7. 示例用法#
登录页面集成效果#

AJAX 验证实现#
// 前端AJAX验证
$("#captchaInput").blur(function() {
const userInput = $(this).val();
const key = $("#captchaKey").val();
$.post("/api/validatecaptcha", { key, userInput })
.done(function(valid) {
if(!valid) {
$("#captchaError").text("验证码不正确");
}
});
});// 后端API接口
[ApiController]
[Route("api/validatecaptcha")]
public class CaptchaApiController : ControllerBase
{
[HttpPost]
public IActionResult Validate([FromBody] CaptchaRequest request)
{
var isValid = _captchaService.ValidateCaptcha(request.Key, request.Input);
return Ok(isValid);
}
}8. 完整代码参考#
GitHub仓库地址
(包含完整可运行项目示例)
核心服务类完整代码:
public class CaptchaService : ICaptchaService
{
private readonly IMemoryCache _cache;
private const string CachePrefix = "Captcha_";
private readonly ObjectPool<Random> _randomPool;
public CaptchaService(IMemoryCache memoryCache)
{
_cache = memoryCache;
_randomPool = ObjectPool.Create<Random>();
}
public byte[] GenerateCaptcha(out string captchaText, out string cacheKey)
{
using var leasedRandom = _randomPool.Get();
var random = leasedRandom.Item;
captchaText = GenerateRandomText(random);
cacheKey = Guid.NewGuid().ToString("N");
_cache.Set($"{CachePrefix}{cacheKey}", captchaText,
new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(5)));
return GenerateImage(captchaText, random);
}
private byte[] GenerateImage(string text, Random random)
{
using var bitmap = new Bitmap(120, 40);
using (var graphics = Graphics.FromImage(bitmap))
{
graphics.Clear(Color.White);
DrawText(graphics, text, random);
DrawNoise(bitmap, random);
DrawLines(graphics, random);
}
ApplyDistortion(bitmap, random);
using var stream = new MemoryStream();
bitmap.Save(stream, ImageFormat.Png);
return stream.ToArray();
}
// 其他辅助方法...
}参考#
希望这篇详细教程能帮助您在ASP.NET Core项目中成功实现图形验证码功能。如在实际使用中遇到问题或有优化建议,欢迎在评论区交流!