一開始接觸驗證碼,覺得很麻煩,每次登陸還得輸入驗證碼,甚是麻煩,不是說過一切爲了人民服務嗎?爲什麼不給用戶帶來方便,我想沒有哪位用戶是願意輸入驗證碼的,特別是對於視力不是太好的朋友們,有時候很變態,明明記得是輸入對了,但是最後顯示的是驗證碼輸入錯誤,這是否跟咱們的軟件爲人民服務衝突呢?最爲難的是春運的時候買火車票,很多人都是買到票之後,由於驗證碼輸入錯誤或者提交失敗一直到提交成功的時候,顯示的卻是票不足,購票不成功,這多傷人啊。爲此,我百思不得其解。直到那一天……
我恍然發現,咱們想的都是太自私了,都是爲自己的方便着想,沒有想到程序員的難處,當然程序員是想讓大家方便,但是沒有事事都是兩全其美的,所以爲了大家方便的同時,程序員遇到更大的一個難題就是安全,而驗證碼是一種區分用戶是計算機和人的全自動程序,可以防止:惡意破解密碼、刷票、論壇灌水,有效防止某個黑客對某一個特定註冊用戶用特定程序暴力破解方式進行不斷的登陸嘗試,實際上是用驗證碼是現在很多網站通行的方式(比如招商銀行的網上個人銀行,百度社區),程序員利用比較簡易的方式實現了這個功能。到現在也許有很多的用戶反映到登陸輸入驗證碼太麻煩了,所以看到有一些網站是第一次登陸不需要輸入驗證碼,然後你輸入用戶名或者密碼不對的時候,就是登陸不成功的時候,驗證碼纔出來,畢竟那是電腦,沒有人腦那麼靈活,所以程序員就讓給電腦一個靈感,但登陸不成功的時候,爲了安全,就給出驗證碼,這就很好的有效防止某個黑客對某一個特定註冊用戶用特定程序暴力破解方式進行不斷的登陸嘗試了,這就實現了安全。我們看一下網易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="" οnclick="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;
}
}
}
這個登錄驗證碼的窗體已經算完成了,我們看的出來驗證碼其實就是通過一張圖片,來區分用戶和計算機,就能達到很好的防止:惡意破解密碼、刷票、論壇灌水,有效防止某個黑客對某一個特定註冊用戶用特定程序暴力破解方式進行不斷的登陸嘗試;所以爲了咱們的網絡安全,這個驗證碼確實發揮了很大的作用。