随着 B/S 应用系统的兴起,网站登录对安全性的要求也越来越高了,一般情况下都会采用验证码的方式提高网站系统的安全等级。
验证码业务流程
在实现验证码功能之前,先弄明白验证码的业务流程。
- 1、当打开登录界面时,前端就会向后端发送一个验证码数据的网络请求,以获取验证码的图片数据及验证码图片的唯一标记;
- 2、后端接口通过请求,随机产生一个验证码文本字符串,大概为 6 位左右的数字、字母或者汉字等;
- 3、通过生成的字符串生成验证码图片,这个图片包含验证码文本字符串的内容,同时添加噪点、无规则的线条、扭曲文本内容等,这样的操作目的是在保证真人能够识别的情况下防止机器伪装真人识别二维码图片上的内容而进行攻击;
- 4、生成好图片后,将图片转成 Base64 的图片数据,同时生成一个唯一标记的 ID,将唯一标记的 ID 与验证码文本内容缓存到后端服务器中或者数据库中;
- 5、后端向前端响应验证码图片的 Base 数据及唯一标记 ID ,用以用户登录时跟随用户登录信息发送到后端登录接口验证;
- 6、用户登录时,后端接口首先验证验证码是否过期以及合法性,根据用户登录请求携带的验证码图片的唯一标记 ID 获取缓存在后端的验证码跟用户输入的验证码进行比较,相同即验证码正确,否则不予登录。
生成验证码
生成验证码的任务主要是生成一个随机的固定长度的字符串,尽可能保证这个字符串随机即可,下面我将主要利用 Random
类实现生成一个包含数字、大小写字母的验证码。
public static string RandomVerificationCode(int lengths)
{
string[] chars = new string[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "P", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
string code = "";
Random random = new Random();
for (int i = 0; i < lengths; i++)
{
code += chars[random.Next(chars.Length)];
}
return code;
}
C#
中绘制图像
C# 中绘制图像,主要用 Bitmap
、Graphics
两个类即可完成。 Bitmap
用于创建一个 BMP 的图像,Graphics
用于在这个图像上绘制噪点、验证码内容等。以下代码详细展示绘制验证码的过程。
public static Bitmap DrawImage(string code)
{
Color[] colors = {
Color.Red, Color.OrangeRed,Color.SaddleBrown,
Color.LimeGreen,Color.Green,Color.MediumAquamarine,
Color.Blue,Color.MediumOrchid,Color.Black,
Color.DarkBlue,Color.Orange,Color.Brown,
Color.DarkCyan,Color.Purple
};
string[] fonts = { "Verdana", "Microsoft Sans Serif", "Comic Sans MS", "Arial", "宋体" };
Random random = new Random();
// 创建一个 Bitmap 图片类型对象
Bitmap bitmap = new Bitmap(code.Length * 18, 32);
// 创建一个图形画笔
Graphics graphics = Graphics.FromImage(bitmap);
// 将图片背景填充成白色
graphics.Clear(Color.White);
// 绘制验证码噪点
for (int i = 0; i < random.Next(60,80); i++)
{
int pointX = random.Next(bitmap.Width);
int pointY = random.Next(bitmap.Height);
graphics.DrawLine(new Pen(Color.LightGray,1), pointX, pointY, pointX+1, pointY);
}
// 绘制随机线条 1 条
graphics.DrawLine(
new Pen(colors[random.Next(colors.Length)], random.Next(3)),
new Point(
random.Next(bitmap.Width),
random.Next(bitmap.Height)),
new Point(random.Next(bitmap.Width),
random.Next(bitmap.Height)));
// 绘制验证码
for (int i = 0; i < code.Length; i++)
{
graphics.DrawString(
code.Substring(i, 1),
new Font(fonts[random.Next(fonts.Length)], 15, FontStyle.Bold),
new SolidBrush(colors[random.Next(colors.Length)]),
16 * i + 1,
random.Next(0, 5)
);
}
// 绘制验证码噪点
for (int i = 0; i < random.Next(30, 50); i++)
{
int pointX = random.Next(bitmap.Width);
int pointY = random.Next(bitmap.Height);
graphics.DrawLine(new Pen(colors[random.Next(colors.Length)], 1), pointX, pointY, pointX, pointY + 1);
}
// 绘制随机线条 1 条
graphics.DrawLine(
new Pen(colors[random.Next(colors.Length)], random.Next(3)),
new Point(
random.Next(bitmap.Width),
random.Next(bitmap.Height)),
new Point(random.Next(bitmap.Width),
random.Next(bitmap.Height)));
return bitmap;
}
以上方法只是将验证码图片生成为 BMP 图片数据,如果是前端展示的话,还需要将图片数据转换成 Base64 的图片数据,代码如下所示。
public static string BitmapToBase64Str(Bitmap bitmap)
{
using (MemoryStream memoryStream = new MemoryStream())
{
bitmap.Save(memoryStream, ImageFormat.Jpeg);
byte[] bytes = memoryStream.ToArray();
return Convert.ToBase64String(memoryStream.ToArray());
}
}
总结
在这个案例中,验证码的实现难点主要在于对 Bitmap
、Graphics
的使用熟练程度,但在此案例中却使用比较简单,没有特别复杂的内容,如果想要通过代码来实现绘画的话,还是需要有一定的图形想象能力的哦。结合这个案例,我做了一个 WindowsForm
的原码,如文章开头所示,喜欢的朋友可以获取下来,研究学习哦。
GitHub: CaptchaImage GitHub
Gitee: CaptchaImage Gitee