功能需求
- 母版頁內嵌登入區塊
- AJAX 無刷新登入/登出
- 「記住帳號」/「記住密碼」勾選
- 密碼以機器相關鹽值 MD5 加密存 Cookie
- 二次訪問自動驗證 Cookie 免登入
前台母版頁關鍵邏輯
<script src="jquery-1.7.1.min.js"></script>
<script>
function CheckLoginState() {
$.post("Member/Data/GetMemberInfo.ashx?method=CheckLoginStatus", function(data) {
var json = $.parseJSON(data);
if (json.Status && json.UserName) {
// 已登入:隱藏登入框,顯示歡迎區
$("#divNotLogin").hide(); $("#divIsLogin").show();
$("#liLogin").hide(); $("#liLogout").show();
$("#lbUserName").text(json.UserName);
} else if (json.Status && !json.UserName) {
// 僅記住帳號
$("#divNotLogin").show(); $("#divIsLogin").hide();
$("#txtUserName").val(json.UserName);
} else {
// 未登入
$("#divNotLogin").show(); $("#divIsLogin").hide();
}
});
}
$(function() {
CheckLoginState(); // 頁面載入即檢查
$("#aLogin").click(function() {
var data = {
Name: $("#txtUserName").val(),
Pwd: $("#txtPwd").val(),
cbName: $("#cbUserName").is(":checked") ? "1" : "0",
cbPwd: $("#cbPwd").is(":checked") ? "1" : "0"
};
$.post("Member/Data/GetMemberInfo.ashx?method=MemberLogin", data, function(ret) {
var json = $.parseJSON(ret);
if (json.Status) {
$("#divNotLogin").hide(); $("#divIsLogin").show();
$("#liLogin").hide(); $("#liLogout").show();
$("#lbUserName").text(json.UserName);
} else {
alert("帳號密碼錯誤或未信箱驗證!");
}
});
});
});
</script>
後台 HttpHandler (GetMemberInfo.ashx)
public class GetMemberInfo : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
string method = context.Request["method"];
if (!string.IsNullOrEmpty(method)) CallMethod(method, context);
}
void CallMethod(string method, HttpContext context)
{
switch (method)
{
case "MemberLogin": MemberLogin(context); break;
case "CheckLoginStatus": CheckLoginStatus(context); break;
case "MemberLogout": MemberLogout(context); break;
case "SaveMemberInfo": SaveMemberInfo(context); break;
case "CheckExistUserName": CheckExistUserName(context); break;
}
}
// 登入驗證
void MemberLogin(HttpContext context)
{
string name = context.Request["Name"];
string pwd = context.Request["Pwd"];
string saveName = context.Request["cbName"];
string savePwd = context.Request["cbPwd"];
if (Tools.IsValidInput(ref name) && Tools.IsValidInput(ref pwd))
{
var info = new Member { Username = name, Password = pwd };
bool ok = new MemberBLL().MemberLogin(info);
string json = new MemberBLL().WriteJsonForLogin(ok, info.Username);
if (ok)
{
new MemberBLL().RememberUserInfo(info,
new MemberBLL().GetRememberType(saveName, savePwd));
context.Session["UserName"] = info.Username;
}
context.Response.Write(json);
}
}
// 自動登入檢查
void CheckLoginStatus(HttpContext context)
{
string user = (string)context.Session["UserName"];
if (!string.IsNullOrEmpty(user))
{
new MemberBLL().WriteJsonForLogin(true, user);
return;
}
context.Response.Write(new MemberBLL().CheckLoginStatus());
}
// 登出
void MemberLogout(HttpContext context)
{
context.Session.Remove("UserName");
CookieHelper.ClearCookie("UserName");
CookieHelper.ClearCookie("Pwd");
JScript.AlertAndRedirect("安全登出!", "Default.aspx");
}
// ... 其他方法略
}
BLL 核心邏輯
public enum RememberType { RememberName, RememberNameAndPwd, NoRemember }
public RememberType GetRememberType(string saveName, string savePwd)
{
if (saveName == "1" && savePwd == "1") return RememberType.RememberNameAndPwd;
if (saveName == "1") return RememberType.RememberName;
return RememberType.NoRemember;
}
public bool RememberUserInfo(Member info, RememberType type)
{
if (type == RememberType.RememberName)
CookieHelper.SetCookie("UserName", info.Username, DateTime.Now.AddDays(7));
else if (type == RememberType.RememberNameAndPwd)
{
string md5 = Tools.GetMD5(info.Password); // 機器相關鹽值
CookieHelper.SetCookie("UserName", info.Username, DateTime.Now.AddDays(7));
CookieHelper.SetCookie("Pwd", md5, DateTime.Now.AddDays(7));
}
return true;
}
public string CheckLoginStatus()
{
string user = CookieHelper.GetCookieValue("UserName");
string pwd = CookieHelper.GetCookieValue("Pwd");
if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(pwd))
return WriteJsonForLogin(false, user);
string realPwdHash = Tools.GetMD5(new MemberDAL().GetPassword(user));
bool ok = realPwdHash == pwd;
return WriteJsonForLogin(ok, user);
}
public string WriteJsonForLogin(bool status, string user)
{
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
using (var jw = new JsonTextWriter(sw))
{
jw.WriteStartObject();
jw.WritePropertyName("Status"); jw.WriteValue(status);
jw.WritePropertyName("UserName"); jw.WriteValue(user);
jw.WriteEndObject();
}
return sb.ToString();
}
安全性亮點
- 密碼不以明文存 Cookie,改存
MD5(密碼 + 機器鹽)
- 驗證時重新計算雜湊比對,Cookie 竊取也無法直接還原密碼
- SQL 參數化 +
Tools.IsValidInput 防注入
- Session + Cookie 雙重驗證
效果圖

