如果说要做验证码,那不得不提的就是GDI+绘图了。我们都知道验证码是以图片形式展示的,而且是动态生成的,这样就需要我们去画出它。
科普一下,什么是GDI+?
GDI+是图形设备接口(GDI)的高级版本, 提供了各种丰富的图形图像处理功能。GDI+主要由二维矢量图形、图像处理和版式3部分组成。GDI+为使用各种字体、字号和样式来显示文本这种复杂任务提供了大量的支持。
下面说说验证码,对于验证码这样的图片,我觉得是由两部分组成的,一部分是矩形的背景,另一部分是在其上的字母数字组合(有的时候有汉字,有的时候是纯字母或者纯数字,这个没有统一规定,怎么选择看你~)。对于矩形的背景我们可以直接把其当成画布,字母数字组合呢?我们可以利用随机数去拼出一组新组合。这样整个过程我们都想好了,下面看下代码吧:
声明一下,我写的这个验证码为5个字符长度,由大小写英文字母+数字随机组合。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
private
readonly
char
[] constant = {
'0'
,
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
,
'7'
,
'8'
,
'9'
,
'a'
,
'b'
,
'c'
,
'd'
,
'e'
,
'f'
,
'g'
,
'h'
,
'i'
,
'j'
,
'k'
,
'l'
,
'm'
,
'n'
,
'o'
,
'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'
,
'O'
,
'P'
,
'Q'
,
'R'
,
'S'
,
'T'
,
'U'
,
'V'
,
'W'
,
'X'
,
'Y'
,
'Z'
};
//一个由数字和大小写英文字母组成的字符数组
protected
void
Page_Load(
object
sender, EventArgs e)
{
Bitmap bitmap =
new
Bitmap(100, 25);
//创建一个位图,宽100,高25,就是我所说的第一部分,矩形背景
Graphics g = Graphics.FromImage(bitmap);
//创建画布
g.Clear(Color.YellowGreen);
//为画布填充黄绿色
Font font1 =
new
Font(
"Arial"
, 15);
//设置字体类型和大小
Brush brush =
new
SolidBrush(Color.Blue);
//设置画刷颜色
Pen myPen =
new
Pen(Color.Blue, 5);
//创建画笔对象
StringBuilder random =
new
StringBuilder(5);
//创建可变字符串对象,用于存放随机生成的验证码
Random rd =
new
Random();
//创建一个随机数生成器对象
for
(
int
i = 0; i < random.Capacity; i++)
{
random.Append(constant[rd.Next(62)]);
//生成一个随机字符加到random里
}
g.DrawString(random.ToString(), font1, brush, 10, 5);
//在画布上画出字符串
System.IO.MemoryStream ms =
new
System.IO.MemoryStream();
//创建数据流MemoryStream
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
//指定图像的输出格式为gif
Response.ClearContent();
Response.ContentType =
"image/Gif"
;
Response.BinaryWrite(ms.ToArray());
//输出二进制数据流
}
|
生成的效果是这样的:
大家可能会觉得这样看起来很容易辨识,跟我们平时登录网站时输入的验证码比起来有点像个小学生。当然,我们可以做些改变,比较加上一定的角度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
private
readonly
char
[] constant = {
'0'
,
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
,
'7'
,
'8'
,
'9'
,
'a'
,
'b'
,
'c'
,
'd'
,
'e'
,
'f'
,
'g'
,
'h'
,
'i'
,
'j'
,
'k'
,
'l'
,
'm'
,
'n'
,
'o'
,
'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'
,
'O'
,
'P'
,
'Q'
,
'R'
,
'S'
,
'T'
,
'U'
,
'V'
,
'W'
,
'X'
,
'Y'
,
'Z'
};
//一个由数字和大小写英文字母组成的字符数组
protected
void
Page_Load(
object
sender, EventArgs e)
{
Bitmap bitmap =
new
Bitmap(100, 25);
//创建一个位图,宽100,高25,就是我所说的第一部分,矩形背景
Graphics g = Graphics.FromImage(bitmap);
//创建画布
g.Clear(Color.YellowGreen);
//为画布填充黄绿色
Font font1 =
new
Font(
"Arial"
, 15);
//设置字体类型和大小
float
angle = 60;
//旋转的一个基础角度
float
length = 0;
//显示字符的基础位置,往后看
Brush brush =
new
SolidBrush(Color.Blue);
//设置画刷颜色
Pen myPen =
new
Pen(Color.Blue, 5);
//创建画笔对象
StringBuilder random =
new
StringBuilder(5);
//创建可变字符串对象,用于存放随机生成的验证码
Random rd =
new
Random();
//创建一个随机数生成器对象
for
(
int
i = 0; i < random.Capacity; i++)
{
random.Append(constant[rd.Next(62)]);
//生成一个随机字符加到random里
g.ResetTransform();
//将画布重置矩阵
SizeF size = g.MeasureString(random[random.Length - 1].ToString(), font1);
//得到新生成字符的尺寸
g.TranslateTransform(length + size.Width / 2, size.Height / 2);
//选择此次旋转的中心位置
g.RotateTransform((
float
)rd.NextDouble() * angle * 2 - angle);
//进行随机角度旋转
g.DrawString(random[random.Length - 1].ToString(), font1, brush,
new
PointF(-size.Width / 2, -size.Height / 2));
//注意,这里不是前一个例子,一次性把5个字符全部画出来,而是一个一个画
length += size.Width;
//保证下次画字符的位置不会覆盖前一次的字符
}
System.IO.MemoryStream ms =
new
System.IO.MemoryStream();
//创建数据流MemoryStream
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
//指定图像的输出格式为gif
Response.ClearContent();
Response.ContentType =
"image/Gif"
;
Response.BinaryWrite(ms.ToArray());
//输出二进制数据流
}
|
这时生成的效果是这样的:
是不是看起来更专业一些了呢?如果大家还是觉得不满意的话,可以看下GDI+的相关内容,通过添加一些噪点的元素,或者删除线这样子的东西来达到提高识别难度的目的,我这里就不一一列举啦。
关于如何画验证码我们说过了,但是还有两点问题我还是想多说一下。
1、我们实际输出的是一个二进制的流,如何做到显示到页面上与页面其他元素共存呢?
这里通用的一种方法就是把画验证码的这段代码放到一个独立的Web窗体页中,在另一个需要显示验证码的页面放一个<img>元素,把其src属性指向该验证码页的url。比如我写的一段是这样
1
|
<asp:Image ID=
"image_validatecode"
runat=
"server"
ImageUrl=
"~/PublicMethod/ValidateCode.aspx"
style=
"padding-left:3px"
/>
|
其实这里我是用大家通用的方法,不过我先前也有单独写一个web自定义控件,专门生成验证码使用,但是当拖入到页面中后运行,它还是会把页面其他元素给覆盖掉,具体原因我也不清楚。
2、验证码主要目的还是用于验证使用的,所以我们在用户名,密码是否合法外,同时也要判断当前输入的验证码是不是与图片上的验证码一致。
我上面的代码中并没有写这块,其实只要在随机生成最终验证码之后,把其值存入一个session中去就可以了。然后在判断用户名,密码的同时去比较一下这个session值就OK了。如:
1
|
Session[
"login_validate_code"
] = random.ToString();
|
3、如何用户没有看清此张验证码,想换一张如何实现?
可以通过脚本给img元素的src属性重新赋值url实现,当然,麻烦点的话也可以使用ajax去实现。大家可以自己试试。