較為周全的Asp.net提交驗證方案(Session版)
此前我介紹了使用數據庫實現的提交驗證方案,一些朋友懷疑其效率不佳,認為Session是更好的方案。
的確使用Session也不會消耗太多內存,而且如今內存白菜價,最不濟就隨手買個2G的插上也就夠了,所以我將在此寫下Session版的實現提要,其余細節參考前篇。
實現方案簡述:
在Session中存儲一個哈希表用以記錄該用戶的每一條驗證信息,哈希表的鍵為驗證信息的過期時間,值為驗證碼的明文。
過期時間使用ViewState存儲,以發給客戶端,并在提交時獲取,以讀取對應的驗證碼明文。
傳給驗證碼生成頁面的ID參數是經ToFileTime()方法轉換的過期時間,驗證碼生成頁獲取到此參數后進行逆轉換,再讀取對應的驗證碼明文以生成顯示。
代碼講解:
先建立一個靜態類,名為“提交驗證”,將用于存儲驗證信息的Session變量封裝為一個屬性:
///
/// 驗證信息表
///
static Hashtable 驗證信息
{
get
{
return Core.函數庫.網絡.Session["驗證信息"] as Hashtable;
}
set
{
Core.函數庫.網絡.Session["驗證信息"] = value;
}
}
清理方法,用于將過期的數據清除:
///
/// 清理所有過期的驗證信息
///
public static void 清理()
{
if (驗證信息 == null || 驗證信息.Count < 5) return;
foreach (DictionaryEntry f in (Hashtable)驗證信息.Clone())
{
if ((DateTime)f.Key < DateTime.Now) 驗證信息.Remove(f);
}
}
小于5條驗證信息則忽略。
克隆一個驗證信息表供foreach使用,如果使用原表循環的話,直接移除內容會改變表長度,從而引發異常。
添加方法:
///
/// 添加一個新的驗證信息。
///
/// 驗證碼">要保存的驗證碼
/// 過期時間差值">用于計算過期時間,單位為分鐘
/// 過期時間戳
public static DateTime 添加(string 驗證碼, byte 過期時間差值)
{
清理();
var 過期時間 = DateTime.Now.AddMinutes(過期時間差值);
if (驗證信息 == null) 驗證信息 = new Hashtable();
驗證信息.Add(過期時間, 驗證碼);
return 過期時間;
}
在添加前進行過期信息清理工作。
獲取、驗證、移除方法:
///
/// 根據過期時間戳獲取對應的驗證碼
///
/// 過期時間戳">驗證信息過期時間戳
/// 驗證碼明文
public static string 獲取(DateTime 過期時間戳)
{
return 驗證信息[過期時間戳] as string;
}
///
/// 驗證用戶輸入的驗證碼是否正確
///
/// 過期時間戳">驗證信息過期時間戳
/// 驗證碼">用戶輸入的驗證碼
/// 返回錯誤信息,如驗證成功則返回null
public static string 驗證(DateTime 過期時間戳, string 驗證碼)
{
if (過期時間戳 < DateTime.Now) return "驗證信息已過期";
var 驗證碼明文 = 獲取(過期時間戳);
if (驗證碼明文 == null) return "驗證信息無效或已過期";
else if (驗證碼明文.ToLower() != 驗證碼.ToLower()) return "驗證碼錯誤";
else return null;
}
///
/// 根據過期時間戳移除對應的驗證信息
///
/// 過期時間戳">驗證信息過期時間戳
public static void 移除(DateTime 過期時間戳)
{
驗證信息.Remove(過期時間戳);
}
使用方法:
使用時在頁面上封裝一個基于ViewState屬性:
///
/// 時間戳屬性,基于ViewState
///
public DateTime? 時間戳
{
get
{
return ViewState["時間戳"] as DateTime?;
}
set
{
ViewState["時間戳"] = value;
}
}
然后在load事件中調用:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
時間戳 = 提交驗證.添加();
Image1.ImageUrl = "~/VerifyImage.aspx?ID=" + 時間戳.Value.ToFileTime();
}
}
(無參數的“添加”方法是我實現的一個適配器封裝方法,采用默認的過期時間設置,隨機生成驗證碼)
提交時的調用:
protected void Button1_Click(object sender, EventArgs e)
{
var s = 提交驗證.驗證(時間戳.Value, TextBox1.Text);
if (s == null)
{
CustomValidator1.IsValid = true;
//提交...
提交驗證.移除(時間戳.Value);
}
else
{
CustomValidator1.IsValid = false;
CustomValidator1.ErrorMessage = s;
}
}
提示:
驗證碼生成時,建議只采用這些字符:2345678abcdefghijkmnprstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ
這里放棄了一些容易產生視覺混淆的字符,比如1和I、l,0和O、o,減少用戶撓墻、砸電腦的可能。