首先背景是exchange的郵箱系統沒有后臺源代碼。因為這個原因,生成驗證碼的機制放在aspx的runat="sever"后臺代碼里面。
首先需要找到iis中logon.aspx文件。在這里找到輸入郵箱名和密碼的input元素,對應增加上輸入驗證碼的input和顯示驗證碼圖片的img元素。
需要增加兩個文件:VerifyCode.aspx是用戶輸入進行輸入的驗證碼驗證操作的代碼;GetImg.aspx是用于顯示驗證碼圖片的,即將之前添加的img的src設置為這個GetImg.aspx即可。至于點擊img之后自動刷新,則屬于體驗性的改進。
代碼具體執行邏輯是GetImg在load的時候,將隨機生成的驗證碼加密之后,存在客戶端瀏覽器的cookie中,同時創建一個Img對象,將4位驗證碼字符按順序輸出到img對象上,同時img隨機分布一些點pixel,之后圖片的stream返回到瀏覽器上。
而VerifyCode的驗證則是則根據輸入的內容和之前的cookie存的密文解密之后進行比較,如果一致,則通過驗證。
VerifyCode.aspx代碼如下:
<%@ Page Language="C#" AutoEventWireup="true" Debug="true" %><%@ Import Namespace="System.Security.Cryptography" %><script runat="server"> PRotected void Page_Load(object sender, EventArgs e) { if (Request.Cookies["yzmCode"] != null && Request.QueryString["yzmc"] != null) { string code = Decrypt(Request.Cookies["yzmCode"].Value).ToUpper(); //Response.Write("code"+code+"/n"); //Response.Write("code"+code+"/n"); //Response.End(); string ucode = Request.QueryString["yzmc"].ToUpper(); if (code == ucode) { Response.Write("ok"); Response.End(); } else { Response.Write("error"); Response.End(); } } else { Response.Write("error2"); Response.End(); } } public static string Decrypt(string Text) { string sKey = "Exchange"; DESCryptoServiceProvider des = new DESCryptoServiceProvider(); int len; len = Text.Length / 2; byte[] inputByteArray = new byte[len]; int x, i; for (x = 0; x < len; x++) { i = Convert.ToInt32(Text.Substring(x * 2, 2), 16); inputByteArray[x] = (byte)i; } des.Key = ASCIIEncoding.ASCII.GetBytes(System.Web.Security.FormsAuthentication.HashPassWordForStoringInConfigFile(sKey, "md5").Substring(0, 8)); des.IV = ASCIIEncoding.ASCII.GetBytes(System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(sKey, "md5").Substring(0, 8)); System.IO.MemoryStream ms = new System.IO.MemoryStream(); CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write); cs.Write(inputByteArray, 0, inputByteArray.Length); cs.FlushFinalBlock(); return Encoding.Default.GetString(ms.ToArray()); }</script>
GetImg.aspx代碼如下:
<%@ Page Language="C#" AutoEventWireup="true" %><%@ Import Namespace="System.Drawing" %><%@ Import Namespace="System.Drawing.Imaging" %><%@ Import Namespace="System.IO" %><%@ Import Namespace="System.Security.Cryptography" %><script runat="server"> public static string Encrypt(string Text) { string sKey = "Exchange"; DESCryptoServiceProvider des = new DESCryptoServiceProvider(); byte[] inputByteArray; inputByteArray = Encoding.Default.GetBytes(Text); des.Key = ASCIIEncoding.ASCII.GetBytes(System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(sKey, "md5").Substring(0, 8)); des.IV = ASCIIEncoding.ASCII.GetBytes(System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(sKey, "md5").Substring(0, 8)); System.IO.MemoryStream ms = new System.IO.MemoryStream(); CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write); cs.Write(inputByteArray, 0, inputByteArray.Length); cs.FlushFinalBlock(); StringBuilder ret = new StringBuilder(); foreach (byte b in ms.ToArray()) { ret.AppendFormat("{0:X2}", b); } return ret.ToString(); } public static string Decrypt(string Text) { string sKey = "Exchange"; DESCryptoServiceProvider des = new DESCryptoServiceProvider(); int len; len = Text.Length / 2; byte[] inputByteArray = new byte[len]; int x, i; for (x = 0; x < len; x++) { i = Convert.ToInt32(Text.Substring(x * 2, 2), 16); inputByteArray[x] = (byte)i; } des.Key = ASCIIEncoding.ASCII.GetBytes(System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(sKey, "md5").Substring(0, 8)); des.IV = ASCIIEncoding.ASCII.GetBytes(System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(sKey, "md5").Substring(0, 8)); System.IO.MemoryStream ms = new System.IO.MemoryStream(); CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write); cs.Write(inputByteArray, 0, inputByteArray.Length); cs.FlushFinalBlock(); return Encoding.Default.GetString(ms.ToArray()); } protected void Page_Load(object sender, EventArgs e) { int codeW = 80; int codeH = 22; int fontSize = 16; string chkCode = string.Empty; Color[] color = { Color.Black, Color.Red, Color.Blue, Color.Green, Color.Orange, Color.Brown, Color.Brown, Color.DarkBlue }; string[] font = { "Times New Roman", "Verdana", "Arial", "Gungsuh", "Impact" }; char[] character = { '2', '3', '4', '5', '6', '8', '9', 'a', 'b', 'd', 'e', 'f', 'h', 'k', 'm', 'n', 'r', 'x', 'y', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W', 'X', 'Y' }; Random rnd = new Random(); for (int i = 0; i < 4; i++) { chkCode += character[rnd.Next(character.Length)]; } //session["yzmCode"] = chkCode; HttpCookie cook = new HttpCookie("yzmCode", Encrypt(chkCode)); cook.Expires = DateTime.Now.AddMinutes(20); Response.Cookies.Add(cook); Bitmap bmp = new Bitmap(codeW, codeH); Graphics g = Graphics.FromImage(bmp); g.Clear(Color.White); for (int i = 0; i < 1; i++) { int x1 = rnd.Next(codeW); int y1 = rnd.Next(codeH); int x2 = rnd.Next(codeW); int y2 = rnd.Next(codeH); Color clr = color[rnd.Next(color.Length)]; g.DrawLine(new Pen(clr), x1, y1, x2, y2); } for (int i = 0; i < chkCode.Length; i++) { string fnt = font[rnd.Next(font.Length)]; Font ft = new Font(fnt, fontSize); Color clr = color[rnd.Next(color.Length)]; g.DrawString(chkCode[i].ToString(), ft, new SolidBrush(clr), (float)i * 18 + 2, (float)0); } for (int i = 0; i < 100; i++) { int x = rnd.Next(bmp.Width); int y = rnd.Next(bmp.Height); Color clr = color[rnd.Next(color.Length)]; bmp.SetPixel(x, y, clr); } Response.Buffer = true; Response.ExpiresAbsolute = System.DateTime.Now.AddMilliseconds(0); Response.Expires = 0; Response.CacheControl = "no-cache"; Response.AppendHeader("Pragma", "No-Cache"); MemoryStream ms = new MemoryStream(); try { bmp.Save(ms, ImageFormat.Png); Response.ClearContent(); Response.ContentType = "image/Png"; Response.BinaryWrite(ms.ToArray()); } finally { bmp.Dispose(); g.Dispose(); } }</script>
注意:加密解密對應的sKey變量要一致。
判斷驗證碼是否輸入正確的js邏輯(簡單點描述就是發起get請求,地址是上面提到的VerifyCode.aspx,參數和代碼中對應上即可):
var codeVaule = $("#yzm").val();if(codeVaule == ""){ $("#yzm-tip").html("驗證碼不能為空"); return false;}else if(codeVaule.length!=4){ $("#yzm-tip").html("驗證碼位數不正確"); return false;}else{ $.get("VerifyCode.aspx?yzmc="+codeVaule,{},function(data){ if(data=="ok"){ $("#yzm-tip").html("驗證碼正確"); //$(".btnSignin").click(); clkLgn();//登錄邏輯 bo=true; }else{ $("#yzm-tip").html("驗證碼錯誤!"); } });}return bo;
(為了簡單,使用了jquery,引用即可)
觸發登錄操作的js函數是clkLgn(),這個放在flogon.js這個腳本文件里面,由于存在點擊登錄按鈕和直接回車(e.keyCode == 13)直接執行登錄的兩種場景,因此此處需要看清js代碼。具體倒沒什么難度。
可以修改clkLgn()的代碼邏輯,直接在里面增加對輸入驗證碼進行驗證的邏輯,然后確定是否走真正的登錄的代碼。也可以在點擊登錄按鈕或者回車的兩處邏輯上分別走驗證請求再處理。
本身加上驗證碼的目的只是為了防止密碼撞庫,快速達到要求即可。
點擊驗證碼圖片自動刷新的改進:
function refreshImg(){ $("#yzmImg")[0].src="GetImg.aspx?"+Math.random(); }
新聞熱點
疑難解答