ASP.NET MVC 验证码演示:从基础实现到最佳实践
验证码(Completely Automated Public Turing test to tell Computers and Humans Apart,全自动区分计算机和人类的公开图灵测试)是Web应用中抵御自动化攻击的基础手段。它通过要求用户识别扭曲的字符、图片或完成简单任务,有效防止机器人批量注册、登录、刷评论等行为。
在ASP.NET MVC中,验证码的实现方式主要分为两类:
- 自定义实现:通过GDI+生成图片验证码(灵活可控);
- 第三方库:如CaptchaMvc(快速集成,减少重复代码)。
本文将从基础概念→手动实现→进阶优化→最佳实践,逐步讲解ASP.NET MVC中验证码的完整方案,并提供可直接运行的代码示例。
目录#
- 基础概念与环境准备
- 自定义验证码:从0到1实现
- 2.1 验证码生成类(核心逻辑)
- 2.2 控制器处理(返回图片流)
- 2.3 视图整合(显示与刷新)
- 2.4 验证逻辑(对比用户输入)
- 进阶优化:提升安全性与用户体验
- 3.1 禁止缓存(避免验证码重复)
- 3.2 增强验证码复杂度(干扰线、旋转、噪点)
- 3.3 支持移动端适配
- 3.4 分布式Session(集群环境兼容)
- 第三方库:CaptchaMvc快速集成
- 最佳实践汇总
- 常见问题排查
- 参考资料
1. 基础概念与环境准备#
1.1 核心概念#
- 验证码文本:随机生成的字符串(字母+数字混合,长度4-6位最佳);
- 图片渲染:通过GDI+将文本绘制成带干扰的图片(防止OCR识别);
- 状态存储:将验证码文本存储在Session中(或分布式缓存),用于后续验证;
- 验证逻辑:对比用户输入与存储的文本,一致则通过。
1.2 环境要求#
- 开发工具:Visual Studio 2019+(或Rider);
- .NET版本:.NET Framework 4.7.2+ 或 .NET Core 3.1+(本文以.NET Framework 4.8为例);
- 依赖:GDI+(Windows系统自带,用于图形绘制)。
2. 自定义验证码:从0到1实现#
2.1 验证码生成类(核心逻辑)#
首先创建CaptchaGenerator类,负责生成随机文本和验证码图片。
代码实现#
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
public class CaptchaGenerator
{
// 验证码字符集(排除易混淆字符:0/O、1/I)
private const string CharSet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
private readonly Random _random = new Random();
/// <summary>
/// 生成随机验证码文本
/// </summary>
/// <param name="length">文本长度</param>
/// <returns>随机字符串</returns>
public string GenerateRandomText(int length = 4)
{
return new string(Enumerable.Repeat(CharSet, length)
.Select(s => s[_random.Next(s.Length)]).ToArray());
}
/// <summary>
/// 生成验证码图片
/// </summary>
/// <param name="captchaText">验证码文本</param>
/// <param name="width">图片宽度</param>
/// <param name="height">图片高度</param>
/// <returns>验证码图片(Bitmap)</returns>
public Bitmap GenerateCaptchaImage(string captchaText, int width = 200, int height = 50)
{
var bitmap = new Bitmap(width, height);
using (var g = Graphics.FromImage(bitmap))
{
// 1. 初始化绘图设置(抗锯齿、高质量渲染)
g.SmoothingMode = SmoothingMode.AntiAlias;
g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
// 2. 绘制渐变背景(避免纯色易被切割)
using (var gradientBrush = new LinearGradientBrush(
new Rectangle(0, 0, width, height),
Color.FromArgb(240, 245, 250), // 浅蓝
Color.FromArgb(220, 230, 240), // 浅灰蓝
15f))
{
g.FillRectangle(gradientBrush, 0, 0, width, height);
}
// 3. 绘制干扰线(15条随机颜色/位置)
for (int i = 0; i < 15; i++)
{
var pen = new Pen(
Color.FromArgb(_random.Next(150, 200), _random.Next(150, 200), _random.Next(150, 200)),
1f);
int x1 = _random.Next(width);
int y1 = _random.Next(height);
int x2 = _random.Next(width);
int y2 = _random.Next(height);
g.DrawLine(pen, x1, y1, x2, y2);
pen.Dispose();
}
// 4. 绘制验证码文本(随机旋转、颜色)
var font = new Font("Microsoft YaHei", 24f, FontStyle.Bold | FontStyle.Italic);
var textBrush = new SolidBrush(
Color.FromArgb(_random.Next(50, 100), _random.Next(50, 100), _random.Next(150, 200))); // 深蓝系
var textSize = g.MeasureString(captchaText, font);
// 随机旋转(±15度)
float rotation = _random.Next(-15, 15);
g.TranslateTransform(
(width - textSize.Width) / 2,
(height - textSize.Height) / 2); // 居中
g.RotateTransform(rotation);
g.DrawString(captchaText, font, textBrush, 0, 0);
g.ResetTransform(); // 重置旋转
// 5. 绘制噪点(200个随机像素)
for (int i = 0; i < 200; i++)
{
int x = _random.Next(width);
int y = _random.Next(height);
bitmap.SetPixel(x, y, Color.FromArgb(_random.Next(100, 200), _random.Next(100, 200), _random.Next(100, 200)));
}
// 6. 绘制边框(灰色细边)
g.DrawRectangle(new Pen(Color.LightGray, 1f), 0, 0, width - 1, height - 1);
}
return bitmap;
}
}2.2 控制器处理(返回图片流)#
创建CaptchaController,实现GetCaptcha Action,负责:
- 生成验证码文本;
- 存储文本到Session;
- 生成图片并返回给客户端。
代码实现#
using System.Drawing.Imaging;
using System.IO;
using System.Web.Mvc;
public class CaptchaController : Controller
{
public ActionResult GetCaptcha()
{
var generator = new CaptchaGenerator();
string captchaText = generator.GenerateRandomText(4); // 4位验证码
// 存储验证码到Session(用于后续验证)
Session["CaptchaText"] = captchaText;
// 关键:禁止浏览器缓存验证码(否则刷新页面验证码不变)
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetExpires(DateTime.UtcNow.AddHours(-1));
Response.Cache.SetNoStore();
// 生成图片并返回
using (var bitmap = generator.GenerateCaptchaImage(captchaText))
using (var ms = new MemoryStream())
{
bitmap.Save(ms, ImageFormat.Jpeg);
return File(ms.ToArray(), "image/jpeg"); // 返回JPEG图片流
}
}
}2.3 视图整合(显示与刷新)#
在登录/注册视图中,通过img标签加载验证码图片,并添加刷新按钮(用JavaScript避免缓存)。
视图代码(Login.cshtml)#
<div class="form-group">
<label>验证码</label>
<div class="captcha-box">
<!-- 加载验证码图片 -->
<img id="captchaImg" src="@Url.Action("GetCaptcha", "Captcha")" alt="验证码" class="captcha-img" />
<!-- 刷新按钮 -->
<button type="button" id="refreshBtn" class="btn btn-light">↻ 刷新</button>
</div>
<input type="text" id="Captcha" name="Captcha" class="form-control mt-2" placeholder="请输入验证码" required />
</div>
<!-- 刷新逻辑:添加时间戳参数避免缓存 -->
<script>
document.getElementById("refreshBtn").addEventListener("click", function () {
var captchaImg = document.getElementById("captchaImg");
// 加时间戳参数,强制浏览器请求新图片
captchaImg.src = "@Url.Action("GetCaptcha", "Captcha")?t=" + new Date().getTime();
});
</script>2.4 验证逻辑(对比用户输入)#
在登录/注册的Action中,获取用户输入的验证码,与Session中的值对比。
登录控制器代码(AccountController)#
public class AccountController : Controller
{
[HttpPost]
public ActionResult Login(string username, string password, string Captcha)
{
// 1. 验证验证码(优先验证,减少无效数据库查询)
string storedCaptcha = Session["CaptchaText"] as string;
if (string.IsNullOrEmpty(storedCaptcha) ||
!string.Equals(Captcha, storedCaptcha, StringComparison.OrdinalIgnoreCase))
{
ModelState.AddModelError("Captcha", "验证码错误");
return View();
}
// 2. 验证用户名密码(省略实际逻辑)
if (username == "admin" && password == "123456")
{
return RedirectToAction("Index", "Home");
}
ModelState.AddModelError("", "用户名或密码错误");
return View();
}
}3. 进阶优化:提升安全性与用户体验#
3.1 禁止缓存(关键优化)#
浏览器默认会缓存图片资源,导致刷新页面后验证码不变。需在GetCaptcha Action中添加以下响应头:
Response.Cache.SetCacheability(HttpCacheability.NoCache); // 不缓存
Response.Cache.SetExpires(DateTime.UtcNow.AddHours(-1)); // 过期时间设为过去
Response.Cache.SetNoStore(); // 禁止存储到缓存3.2 增强验证码复杂度#
- 字符旋转:通过
RotateTransform随机旋转文本(±15度),增加OCR识别难度; - 渐变背景:避免纯色背景被轻易切割;
- 干扰线/噪点:增加随机线条和像素点,干扰机器识别;
- 字符集优化:排除易混淆字符(如0/O、1/I、L/1),提升用户识别率。
3.3 支持移动端适配#
- 调整验证码图片尺寸(如
width=150、height=40),适配手机屏幕; - 增加图片和输入框的间距,避免误触;
- 使用响应式CSS:
.captcha-img { width: 100%; height: auto; } .captcha-box { display: flex; align-items: center; gap: 10px; }
3.4 分布式Session(集群环境兼容)#
如果应用部署在多服务器集群,默认的InProc Session(进程内存储)会导致不同服务器间Session不共享,验证码验证失败。需改用分布式Session:
- 安装Redis:
Install-Package Microsoft.Web.RedisSessionStateProvider; - 配置Web.config:
<sessionState mode="Custom" customProvider="RedisSessionProvider"> <providers> <add name="RedisSessionProvider" type="Microsoft.Web.Redis.RedisSessionStateProvider" host="localhost" port="6379" accessKey="" ssl="false" /> </providers> </sessionState>
4. 第三方库:CaptchaMvc快速集成#
如果不想手动实现验证码,可使用CaptchaMvc(专为ASP.NET MVC设计的开源库),简化开发流程。
4.1 安装与配置#
- 安装NuGet包:
Install-Package CaptchaMvc.Mvc5 - 在
Global.asax中注册:protected void Application_Start() { // 其他配置... CaptchaMvcConfig.RegisterRoutes(RouteTable.Routes); }
4.2 视图与控制器使用#
- 视图:用
@Html.Captcha()生成验证码:@Html.Captcha("Refresh", "Enter the code", 4) <!-- 4位验证码 --> - 控制器:用
[CaptchaVerify]属性验证:[HttpPost] [CaptchaVerify("Captcha", "验证码错误")] // 验证名为"Captcha"的输入框 public ActionResult Login(string username, string password) { // 验证通过,继续处理 }
5. 最佳实践汇总#
- 优先验证验证码:在用户名密码验证前先验证验证码,减少无效数据库查询;
- Session安全:
- 避免存储敏感信息,仅存储验证码文本;
- 集群环境用分布式Session(如Redis);
- 验证码复杂度:
- 长度≥4位,混合字母+数字;
- 避免易混淆字符(0/O、1/I);
- 用户体验:
- 支持刷新按钮(无需刷新整个页面);
- 忽略大小写验证(
StringComparison.OrdinalIgnoreCase);
- 安全性:
- 禁止缓存验证码;
- 限制验证码有效时间(如5分钟后过期):
// 在GetCaptcha中存储生成时间 Session["CaptchaExpireTime"] = DateTime.Now.AddMinutes(5); // 验证时检查过期时间 if (DateTime.Now > (DateTime)Session["CaptchaExpireTime"]) { ModelState.AddModelError("Captcha", "验证码已过期"); }
6. 常见问题排查#
- 验证码不显示:
- 检查
GetCaptchaAction是否返回图片流(File方法); - 检查
img标签的src路径是否正确(如@Url.Action("GetCaptcha", "Captcha"));
- 检查
- 刷新后验证码不变:
- 未添加时间戳参数(
?t=时间戳); - 未禁止浏览器缓存(缺少
Response.Cache设置);
- 未添加时间戳参数(
- 验证码验证失败:
- Session过期或未存储(检查
Session["CaptchaText"]是否有值); - 大小写敏感(未用
StringComparison.OrdinalIgnoreCase);
- Session过期或未存储(检查
- GDI+报错:
- 字体不存在(服务器上没有
Microsoft YaHei,换为SimSun(宋体)); - 图片资源未释放(确保用
using包裹Bitmap和Graphics)。
- 字体不存在(服务器上没有
7. 参考资料#
- MSDN 文档:GDI+ 图形编程
- ASP.NET MVC 官方文档:Session 状态管理
- CaptchaMvc 官方文档:GitHub 仓库
- OWASP 验证码最佳实践:CAPTCHA 控件
通过本文的实现,你可以快速为ASP.NET MVC应用添加安全、易用的验证码功能。自定义实现适合需要高度定制的场景,第三方库适合快速集成。根据项目需求选择合适的方案,同时遵循最佳实践,确保验证码的安全性和用户体验。