神奇的验证码,我们一起来探究

来源:互联网 发布:阿里云邮箱开启pop3 编辑:程序博客网 时间:2024/05/12 00:39

一开始接触验证码,觉得很麻烦,每次登陆还得输入验证码,甚是麻烦,不是说过一切为了人民服务吗?为什么不给用户带来方便,我想没有哪位用户是愿意输入验证码的,特别是对于视力不是太好的朋友们,有时候很变态,明明记得是输入对了,但是最后显示的是验证码输入错误,这是否跟咱们的软件为人民服务冲突呢?最为难的是春运的时候买火车票,很多人都是买到票之后,由于验证码输入错误或者提交失败一直到提交成功的时候,显示的却是票不足,购票不成功,这多伤人啊。为此,我百思不得其解。直到那一天……

我恍然发现,咱们想的都是太自私了,都是为自己的方便着想,没有想到程序员的难处,当然程序员是想让大家方便,但是没有事事都是两全其美的,所以为了大家方便的同时,程序员遇到更大的一个难题就是安全,而验证码是一种区分用户是计算机和人的全自动程序,可以防止:恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试,实际上是用验证码是现在很多网站通行的方式(比如招商银行的网上个人银行,百度社区),程序员利用比较简易的方式实现了这个功能。到现在也许有很多的用户反映到登陆输入验证码太麻烦了,所以看到有一些网站是第一次登陆不需要输入验证码,然后你输入用户名或者密码不对的时候,就是登陆不成功的时候,验证码才出来,毕竟那是电脑,没有人脑那么灵活,所以程序员就让给电脑一个灵感,但登陆不成功的时候,为了安全,就给出验证码,这就很好的有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试了,这就实现了安全。我们看一下网易163邮箱是怎么做的:

当你多次登陆不成功的时候,他会弹出这么一个窗口:

我们的验证码出来了,这就证明了咱们的验证码确实能有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试进行破解。所以当我们要输入验证码的时候,我们要想到这些,就不必心烦了,程序员想的不是不周到,是想让大家的数据更加安全,也为我们的秘密泄露加上了很好的保护套。

至于春运买火车票的,我看见很多人抱怨说输入验证码导致自己没买到票,这么说吧,要是不输入验证码,那更买不到了,至于人家的铁道部网站做的怎么样,咱们先不讨论。

相信很多人跟我一样,有过这样的背景,其实挺好,没有这样的疑惑,我们就没有进步的动力,就难以跟上时代的步伐。我写这篇博客就是想给大家解开这个迷惑,到底这是怎么做到的呢?

我在想,为什么验证码那么神奇呢?为什么验证码能区分用户是计算机和人呢?他是怎么做到的呢?原因很简单,我们来看看内部的代码运行情况:

我们做一个登陆页面,来看看验证码的背后是怎么工作的:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Login_blog.aspx.cs" Inherits="WebApplication1.Login_blog" %><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head runat="server">    <title>登陆验证码测试</title>    <link href="css/Login_blog.css" rel="stylesheet" type="text/css" /><script language="javascript" type="text/javascript">        function changeCode() {            var imgNode = document.getElementById("vimg");            imgNode.src = "../handler/WaterMark.ashx?t=" + (new Date()).valueOf();  // 这里加个时间的参数是为了防止浏览器缓存的问题           }      </script></head><body>    <form id="form1" runat="server">    <div>        <h3>            登录验证码测试        </h3>        <div id="Login_blog">            <p>                用户名:                <asp:TextBox ID="txtUserName" runat="server" CssClass="textbox"></asp:TextBox>                <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ErrorMessage="请输入用户名"                    Text="*" ControlToValidate="txtUserName"></asp:RequiredFieldValidator>            </p>            <p>                密码:                <asp:TextBox ID="txtPassword" runat="server" TextMode="Password" CssClass="textbox"></asp:TextBox>                <asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" ErrorMessage="请输入密码!"                    Text="*" ControlToValidate="txtPassword"></asp:RequiredFieldValidator>            </p>            <p>                验证码:<img src="../handler/WaterMark.ashx" id="vimg" alt="" onclick="changeCode()" />                <asp:TextBox ID="txtCode" runat="server" CssClass="txtCode"></asp:TextBox>                <asp:RequiredFieldValidator ID="RequiredFieldValidator3" runat="server" ErrorMessage="请输入验证码!"                    Text="*" ControlToValidate="txtCode"></asp:RequiredFieldValidator>            </p>            <p>                <asp:Button ID="btnLogin" runat="server" Text="登录" OnClick="btnLogin_Click" />            </p>            <asp:ValidationSummary ID="ValidationSummary1" runat="server" ShowMessageBox="true"                ShowSummary="false" />        </div>    </div>    </form></body></html>
在Login_blog.aspx.cs里边这样
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.UI;using System.Web.UI.WebControls;using BLL;using System.Web.Security;namespace WebApplication1{    public partial class Login_blog : System.Web.UI.Page    {        protected void Page_Load(object sender, EventArgs e)        {        }        //登录按钮        protected void btnLogin_Click(object sender, EventArgs e)        {            //判断验证码是否输入正确            string code = txtCode.Text.Trim();            string rightCode = Session["Code"].ToString();            if (code!=rightCode)            {                Page.ClientScript.RegisterStartupScript(Page.GetType(), "message", "<script language='javascript' defer>alert('验证码输入错误!');</script>");                return;            }            string name = txtUserName.Text.Trim();            string pwd = txtPassword.Text.Trim();            bool b = LoginManager.Login(name, pwd);            if (b)            {                //登录成功                Session["admin"] = name;                Response.Redirect("http://blog.csdn.net/yi_zz");            }            else            {                    //登录失败                Page.ClientScript.RegisterStartupScript(Page.GetType(), "message", "<script language='javascript' defer>alert('登陆失败,用户名或者密码错误!');</script>");            }        }            }}
css文件中我们排一下版:

/**登陆验证码测试*/     *{    margin :0;    padding :0;    }body {    font-size :14px;}#loginfrm #login p{    padding-bottom :10px;    }.textbox{    width :150px;    }.txtCode{    width :73px;    }
我们在逻辑层写一点判断:
/* * 创建人:宗毅    * 创建时间:2012年8月11日19:38:27 * 说明:登陆的业务逻辑类 * 版权所有:*/using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace BLL{   public  class LoginManager   {       #region 用户登陆是否成功       /// <summary>       /// 用户登陆是否成功       /// </summary>       /// <param name="name">用户名</param>       /// <param name="pwd">密码</param>       /// <returns></returns>       public static bool Login(string name, string pwd)       {           bool flag = false;           if ("zongyi" == name && "czy" == pwd)           {               flag = true;           }           return flag;       }       #endregion   }    }
最后看看验证码这部分的代码:
/* * 验证码*/using System;using System.Web;using System.Drawing;using System.Drawing.Drawing2D;using System.Web.SessionState;public class WaterMark : IHttpHandler, IRequiresSessionState  // 要使用session必须实现该接口,记得要导入System.Web.SessionState命名空间{    public void ProcessRequest(HttpContext context)    {        string checkCode = GenCode(5);  // 产生5位随机字符        context.Session["Code"] = checkCode; //将字符串保存到Session中,以便需要时进行验证        System.Drawing.Bitmap image = new System.Drawing.Bitmap(70, 22);        Graphics g = Graphics.FromImage(image);        try        {            //生成随机生成器            Random random = new Random();            //清空图片背景色            g.Clear(Color.White);            // 画图片的背景噪音线            int i;            for (i = 0; i < 25; i++)            {                int x1 = random.Next(image.Width);                int x2 = random.Next(image.Width);                int y1 = random.Next(image.Height);                int y2 = random.Next(image.Height);                g.DrawLine(new Pen(Color.Silver), x1, y1, x2, y2);            }            Font font = new System.Drawing.Font("Arial", 12, (System.Drawing.FontStyle.Bold));            System.Drawing.Drawing2D.LinearGradientBrush brush = new System.Drawing.Drawing2D.LinearGradientBrush(new Rectangle(0, 0, image.Width, image.Height), Color.Blue, Color.DarkRed, 1.2F, true);            g.DrawString(checkCode, font, brush, 2, 2);            //画图片的前景噪音点            g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1);            System.IO.MemoryStream ms = new System.IO.MemoryStream();            image.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);            context.Response.ClearContent();            context.Response.ContentType = "image/Gif";            context.Response.BinaryWrite(ms.ToArray());        }        finally        {            g.Dispose();            image.Dispose();        }    }    /// <summary>    /// 产生随机字符串    /// </summary>    /// <param name="num">随机出几个字符</param>    /// <returns>随机出的字符串</returns>    private string GenCode(int num)    {        string str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";        char[] chastr = str.ToCharArray();        string code = "";        Random rd = new Random();        int i;        for (i = 0; i < num; i++)        {            //code += source[rd.Next(0, source.Length)];            code += str.Substring(rd.Next(0, str.Length), 1);        }        return code;    }    public bool IsReusable    {        get        {            return false;        }    }}

我们看到这个界面:

这个登录验证码的窗体已经算完成了,我们看的出来验证码其实就是通过一张图片,来区分用户和计算机,就能达到很好的防止:恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试;所以为了咱们的网络安全,这个验证码确实发挥了很大的作用。