文章教程

9.3图片验证码技术

8/31/2020 8:52:25 PM 人评论 次浏览

9.3 图片验证码技术

学习目标

熟悉实现验证码功能的操作步骤。

为了防止用户利用程序、机器人的方式自动注册、登录、灌水、发布信息,大多数网站都采用了验证码技术。所谓验证码,就是将一串随机产生的数字或符号,生成一幅图片,图片里加上一些干扰元素,例如随机画数条直线,画一些点等,由用户肉眼识别其中的验证码信息,输入表单提交网站验证,验证成功后才能使用某项功能。

在ASP.NET环境中,要通过程序的方式在用户登录页面增加验证码功能,可以归纳为如下几个步骤。

(1)随机产生字符串。

(2)把随机生成的字符串转换成图片输出。

(3)使用Session记录随机字符串。

(4)登录页面引用验证码及程序实现。

9.3.1 随机产生字符串

在ASP.NET中,程序产生随机字符串核心使用的就是Random类来完成的。比如下面的代码返回的就是[0,3)之间的一个随机整数,即0、1、2中的某个整数。

Random rand = new Random();
Response.Write(rand.Next(3));

而接下来的代码就返回的是[5,8)之间的一个随机整数,即5、6、7中的某个整数。

Random rand = new Random();
Response.Write(rand.Next(5,8));

其实这里讲的就是Random这个类的Next方法的两个重载形式,当然还有其他形式,不过常用的就是这两个形式。

而验证码这里需要的是由字母、数字组成的字符串,而非纯粹的数字构造的字符串,这就用到了C#数组的功能,不妨定义如下字符串:

string str = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghjklmnopqrstuvwxyz1234567890";

其实定义的字符串变量str在C#语言中也就是一个字符数组,于是可以通过如下代码遍历该字符数组中的每个成员。

string str = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghjklmnopqrstuvwxyz1234567890";
for (int i = 0; i < str.Length; i++)
  {
Response.Write(str[i].ToString ()+"<br />");
}

显然这里用到的字符数组str的索引号本身就是整数,取值范围为0~str.length-1,这样一来通过Random类来获取0~str.length-1范围之间的一个随机数,然后再作为字符数组str的索引号,得到的当然就是一个随机字符了,而要得到多个随机字符构成随机字符串,那就多循环几次即可。接下来就给出完整的生成随机字符串的方法代码。

  //获取一个随机字符串
  public static string GetRandom(int n)
  {
    string str = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghjklmnopqrstu
    vwxyz1234567890";
    Random random = new Random();
    string result = string.Empty;
    for (int i = 1; i <= n; i++)
    {
       result += str[random.Next(0, str.Length - 1)].ToString();
    }
    return result;
  }

通常都是把这个方法放在一个通用功能类Common.cs中,这个需要自己创建,创建也很简单,就是在网站项目中右击,在弹出的快捷菜单中依次选择“添加”|“类”,然后命名为Common即可。

9.3.2 把随机生成的字符串转换成图片输出

要把随机生成的字符串转换成图片输出,这就用到了C#语言GDI+(Graphics Device Interface,图形设备接口)编程。

在ASP.NET中,通常GDI+编程用到下面的这些类。

System.Random
System.Drawing.Bitmap
System.Drawing.SolidBrush
System.Drawing.PointF
System.IO.MemoryStream

接下来给出完整的字符串转换图片输出代码。

  //输出图片
  public static void DisplayImage(string str)
  {
    //创建一个图片区域设置宽度和高度
    Bitmap image = new Bitmap(50, 20);
    //获取这个图片区域,便于在其中添加元素
    Graphics g = Graphics.FromImage(image);
    //设置图片背景为白色
    g.Clear(Color.White);
    //画线构造一些盲点,意思就是故意让图片显示不清楚些
    Random random = new Random();
    for (int i = 0; i < 10; i++)
    {
       int x1 = random.Next(image.Width);
       int y1 = random.Next(image.Height);
       int x2 = random.Next(image.Width);
       int y2 = random.Next(image.Height);
       g.DrawLine(new Pen(Color.Silver), x1, y1, x2, y2);
    }
    //定义字体对象,设置字体和字体大小
    Font font = new Font("黑体", 12);
    //定义画笔颜色
    SolidBrush brush = new SolidBrush(Color.BlueViolet);
    //定义画笔写信息的位置坐标点,左上角为(0,0)点
    PointF point = new PointF(2, 2);
    //写字符串到图片中
    g.DrawString(str, font, brush, point);
    //给图片画边框
    g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width
    - 1, image.Height - 1);
    //生成一个内存流对象,便于存储并输出图片
    MemoryStream ms = new MemoryStream();
    //保存绘制的图片到内存流ms中
    image.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
    //输出二进制图片流
    HttpContext.Current.Response.BinaryWrite(ms.ToArray());
  }

通常都是把这个方法放在一个通用功能类Common.cs中。然后创建一个新的网页CheckCode.aspx,在其程序页面中输入如下代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class CheckCode : System.Web.UI.Page
{
  protected void Page_Load(object sender, EventArgs e)
  {
    Common.DisplayImage(Common.GetRandom(5));
  }
}

然后运行CheckCode.aspx,显示效果如图9-11所示,而且页面每刷新一次,图片中的字符串变化一次,这就是使用Common.GetRandom(5)带来的效果。

图9-11 页面CheckCode.aspx运行显示效果

9.3.3 使用Session记录随机字符串

由于程序在引用上述CheckCode.aspx后,用户在页面上可以看到验证码这个随机字符串,而用户输入后,程序要判断,所以系统每产生一次随机字符串,就需要保存到一个变量中,这里当然用的就是Web的通用方法:Session变量来完成。

通过在DisplayImage方法中的首行增加如下代码:

HttpContext.Current.Session["code"] = str;

这样一来,页面在引用验证码输入后,就可以使用Session["code"]来判断用户输入的验证码是否正确,进而给出相应的提示信息。

9.3.4 登录页面引用验证码及程序实现

登录、注册、发布帖子、写博客、写微博等都会用到验证码效果,这里就以用户登录为例来说明如何引用验证码。

(1)首先需要做一个完整的登录表单页面Login.aspx,如图9-12所示。

图9-12 登录表单页面Login.aspx运行显示效果(1)

接下来给出登录表单页面Login.aspx的完整脚本源代码。

<%@ Page Language="C#" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title>用户登录</title>
  <link href="css/style.css" rel="stylesheet" />
  <script type="text/javascript" src="Scripts/jquery-1.7.1.js"></script>
</head>
  <script type="text/javascript">
    //表单验证代码
    function check() {
       var username = $("#username").val();
       var pwd = $("#pwd").val();
       var code = $("#code").val();
       if (username == "") {
          alert("用户名不能为空!");
          $("#username").focus();
          return;
       }
       if (pwd == "") {
          alert("登录口令不能为空!");
          $("#pwd").focus();
          return;
       }
       if (code == "") {
          alert("验证码不能为空!");
          $("#code").focus();
          return;
       }
       form1.submit();
    }
</script>
<body>
  <form action="dologin.aspx" method="post" id="form1" name="form1">
    <table width="415" border="0" cellspacing="0" cellpadding="0"
style="border: 1px solid #333; margin: 10px;">
       <tr>
          <td height="30" colspan="3" align="center">
          <strong>用户登录</strong></td>
       </tr>
       <tr>
          <td width="89" height="30" align="center">用户名称</td>
          <td height="30" colspan="2">
  <input type="text" id="username" name="username" class="txt" /></td>
       </tr>
       <tr>
          <td height="30" align="center">登录口令</td>
          <td height="30" colspan="2">
              <input type="password" id="pwd" name="pwd"
              class="txt" /></td>
       </tr>
       <tr>
          <td height="30" align="center">输入验证码</td>
          <td width="131" height="30">
  <input type="text" id="code" name="code" class="txt" style=
  "width: 80px;" /></td>
          <td width="193">&nbsp;</td>
       </tr>
       <tr>
          <td height="30" align="center">&nbsp;</td>
          <td height="30" colspan="2">
              <input type="button" name="button" id="button"
value="登录" onclick="check()" style="width: 80px; height: 30px;" />
              <input type="button" name="button" id="button1"
value="重置" onclick="form1.reset()"style="width: 80px; height: 30px;" />
          </td>
       </tr>
    </table>
  </form>
</body>
</html>

(2)其次,需要在mydb数据库中创建一个用户表tb_user,表结构如图9-13所示。

图9-13 用户表tb_user表结构

创建tb_user的完整数据库脚本如下。

create table tb_user
(
id int identity,
username varchar(50) primary key,
pwd varchar(50),
dt datetime default getdate()
)
go
insert into tb_user(username,pwd)values('admin','21232F297A57A5
A743894A0E4A801FC3');
go
select * from tb_user;
go

其中,给表tb _user添加的模拟记录,用户名称为admin,而口令也正是使用前面讲的Get_MD5方法处理字符串admin后的结果,也就是添加的模拟账号/口令为admin/admin。

(3)打开登录页面Login.aspx,给验证码一行的最后一列增加验证码引用代码,下面给出这行的完整代码。

…
<tr>
  <td height="30" align="center">输入验证码</td>
  <td width="131" height="30">
    <input type="text" id="code" name="code" class="txt" style=
    "width: 80px;" /></td>
  <td width="193">
    <img src="CheckCode.aspx" style="cursor:pointer;"
    title="看不清,单击换一张"
    onclick="this.src='CheckCode.aspx?random='+Math.random()" />
    <span style=" color:#ff0000; font-size:12px;">验证码不区分大小写</span>
  </td>
</tr>
…

再次运行页面Login.aspx,显示效果如图9-14所示。

图9-14 登录表单页面Login.aspx运行显示效果(2)

(4)显然,验证码已经引入到登录页面中了,而且也支持“单击换一张”的功能,接下来就是完成dologin.aspx页面,把登录的处理程序页面代码完成。dologin.aspx完整处理代码如下。

protected void Page_Load(object sender, EventArgs e)
{
    string username = Request.Form["username"];
    string pwd = Request.Form["pwd"];
    pwd = Common.Get_MD5(pwd);
    string code = Request.Form["code"].ToLower();
    DBHelper db = new DBHelper();
    //首先判断验证码是否正确
    if (code == Session["code"].ToString().ToLower())
    {
       //接下来检查用户名是否正确
       Hashtable ht = new Hashtable();
       ht.Add("@username", username);
       string sql="select username,pwd from tb_user where
       username=@username";
       DataRow row = db.GetRow(sql, ht);
       if (row == null)
       {
          Response.Write("<script>alert('用户名输入错误!');
          history.back();</script>");
       }
       else
       {
          //接下来检查口令输入是否正确
          if (pwd == row["pwd"].ToString())
          {
              Response.Write("成功登录了!");
          }
          else
          {
              Response.Write("<script>alert('口令输入错误!');
              history.back();</script>");
          }
       }
    }
    else
    {
       Response.Write("<script>alert('验证码输入错误!');
       history.back();</script>");
    }
}

教程类别