神奇的驗證碼,我們一起來探究
一開始接觸驗證碼,覺得很麻煩,每次登陸還得輸入驗證碼,甚是麻煩,不是說過一切為了人民服務嗎?為什麼不給使用者帶來方便,我想沒有哪位使用者是願意輸入驗證碼的,特別是對於視力不是太好的朋友們,有時候很變態,明明記得是輸入對了,但是最後顯示的是驗證碼輸入錯誤,這是否跟咱們的軟體為人民服務衝突呢?最為難的是春運的時候買火車票,很多人都是買到票之後,由於驗證碼輸入錯誤或者提交失敗一直到提交成功的時候,顯示的卻是票不足,購票不成功,這多傷人啊。為此,我百思不得其解。直到那一天……
我恍然發現,咱們想的都是太自私了,都是為自己的方便著想,沒有想到程式設計師的難處,當然程式設計師是想讓大家方便,但是沒有事事都是兩全其美的,所以為了大家方便的同時,程式設計師遇到更大的一個難題就是安全,而驗證碼是一種區分使用者是計算機和人的全自動程式,可以防止:惡意破解密碼、刷票、論壇灌水,有效防止某個黑客對某一個特定註冊使用者用特定程式暴力破解方式進行不斷的登陸嘗試,實際上是用驗證碼是現在很多網站通行的方式(比如招商銀行的網上個人銀行,百度社群),程式設計師利用比較簡易的方式實現了這個功能。到現在也許有很多的使用者反映到登陸輸入驗證碼太麻煩了,所以看到有一些網站是第一次登陸不需要輸入驗證碼,然後你輸入使用者名稱或者密碼不對的時候,就是登陸不成功的時候,驗證碼才出來,畢竟那是電腦,沒有人腦那麼靈活,所以程式設計師就讓給電腦一個靈感,但登陸不成功的時候,為了安全,就給出驗證碼,這就很好的有效防止某個黑客對某一個特定註冊使用者用特定程式暴力破解方式進行不斷的登陸嘗試了,這就實現了安全。我們看一下網易163郵箱是怎麼做的:
當你多次登陸不成功的時候,他會彈出這麼一個視窗:
我們的驗證碼出來了,這就證明了咱們的驗證碼確實能有效防止某個黑客對某一個特定註冊使用者用特定程式暴力破解方式進行不斷的登陸嘗試進行破解。所以當我們要輸入驗證碼的時候,我們要想到這些,就不必心煩了,程式設計師想的不是不周到,是想讓大家的資料更加安全,也為我們的祕密洩露加上了很好的保護套。
至於春運買火車票的,我看見很多人抱怨說輸入驗證碼導致自己沒買到票,這麼說吧,要是不輸入驗證碼,那更買不到了,至於人家的鐵道部網站做的怎麼樣,咱們先不討論。
相信很多人跟我一樣,有過這樣的背景,其實挺好,沒有這樣的疑惑,我們就沒有進步的動力,就難以跟上時代的步伐。我寫這篇部落格就是想給大家解開這個迷惑,到底這是怎麼做到的呢?
我在想,為什麼驗證碼那麼神奇呢?為什麼驗證碼能區分使用者是計算機和人呢?他是怎麼做到的呢?原因很簡單,我們來看看內部的程式碼執行情況:
我們做一個登陸頁面,來看看驗證碼的背後是怎麼工作的:
在Login_blog.aspx.cs裡邊這樣<%@ 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>
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;
}
}
}
我們看到這個介面:
這個登入驗證碼的窗體已經算完成了,我們看的出來驗證碼其實就是通過一張圖片,來區分使用者和計算機,就能達到很好的防止:惡意破解密碼、刷票、論壇灌水,有效防止某個黑客對某一個特定註冊使用者用特定程式暴力破解方式進行不斷的登陸嘗試;所以為了咱們的網路安全,這個驗證碼確實發揮了很大的作用。