技術棧
- 前端:HTML + CSS + jQuery + Ajax
- 後端:ASP.NET Generic Handler (
.ashx) + SQL Server
- 安全:參數化查詢防 SQL 注入、MD5 雙重加密 + 加鹽
前端頁面結構
<body>
<div id="header">
<div id="header_con">
<a href="javascript:;" onclick="showRegBox()">註冊</a>
<a href="javascript:;" onclick="ShowLoginBox()">登入</a>
</div>
</div>
<div id="loginBox" style="display:none">
<input type="text" id="TxtUserName" placeholder="手機/郵箱/用戶名" />
<input type="password" id="TxtPwd" placeholder="請輸入密碼" />
<a href="javascript:;" onclick="login()">登入</a>
</div>
<div id="Regbox" style="display:none">
<input type="text" id="TxtRegUserName" placeholder="手機/郵箱/用戶名" />
<input type="password" id="TxtRegPwd" placeholder="請輸入密碼" />
<input type="text" id="TxtRegqq" placeholder="QQ號" />
<input type="text" id="TxtRegEmail" placeholder="郵箱" />
<a href="javascript:;" onclick="Reglogin()">註冊</a>
</div>
</body>
CSS 樣式要點
* { margin:0; padding:0; }
#header { height:40px; background:#000; width:100%; }
#header a { float:right; color:#fff; line-height:40px; margin-left:10px; }
.login_Item input { width:348px; height:40px; margin-top:10px; border:1px solid #04a6f9; }
.login_Item a { display:block; width:350px; height:40px; background:#04a6f9; color:#fff; text-align:center; line-height:40px; margin-top:20px; }
jQuery + Ajax 邏輯(使用 layer 插件)
function login() {
var username = $.trim($("#TxtUserName").val());
var pwd = $.md5($.trim($("#TxtPwd").val())); // 前端首次 MD5
if (!username || !pwd) { layer.alert("帳號密碼不可為空"); return; }
$.post("/Handler1.ashx", {UserName:username, Pwd:pwd, cmd:"login"}, function(data){
if (data=="登入成功") layer.msg("登入成功!",{icon:6});
else layer.msg("帳號或密碼錯誤",{icon:5});
});
}
function Reglogin() {
var username = $.trim($("#TxtRegUserName").val());
var pwd = $.md5($.trim($("#TxtRegPwd").val()));
var qq = $.trim($("#TxtRegqq").val());
var email = $.trim($("#TxtRegEmail").val());
if (!username || !pwd) { layer.msg("帳號密碼不可為空"); return; }
$.post("/Handler1.ashx", {UserName:username, Pwd:pwd, qq:qq, email:email, cmd:"reg"}, function(data){
if (data=="註冊成功") layer.msg("恭喜註冊成功!",{icon:6});
else layer.msg(data,{icon:5});
});
}
後端 Handler 處理(參數化查詢 + 二次 MD5 + 加鹽)
public class Handler1 : IHttpHandler {
public void ProcessRequest(HttpContext context) {
string cmd = context.Request.Form["cmd"];
string result = cmd=="login" ? LoginAjax() : RegAjax();
context.Response.Write(result);
}
string LoginAjax() {
string username = context.Request.Form["username"];
string pwd = Md5Class.GetMD5(context.Request.Form["pwd"] + "傻逼玩意", 32); // 二次 MD5 + 加鹽
string sql = "SELECT id FROM Users WHERE UserName=@UserName AND Pwd=@Pwd";
SqlParameter[] paras = {
new SqlParameter("@UserName", SqlDbType.NVarChar) { Value = username },
new SqlParameter("@Pwd", SqlDbType.NVarChar) { Value = pwd }
};
return SqlHelper.Exists(sql, paras) ? "登入成功" : "帳號或密碼錯誤";
}
string RegAjax() {
string username = context.Request.Form["username"];
string pwd = Md5Class.GetMD5(context.Request.Form["pwd"] + "傻逼玩意", 32);
string qq = context.Request.Form["qq"];
string email = context.Request.Form["email"];
// 檢查重複
string chkSql = "SELECT id FROM Users WHERE UserName=@UserName";
SqlParameter[] chkParas = { new SqlParameter("@UserName", SqlDbType.NVarChar) { Value = username } };
if (SqlHelper.Exists(chkSql, chkParas)) return "該用戶已註冊";
// 插入新用戶
string insSql = "INSERT INTO Users (UserName,Pwd,QQ,eMail) VALUES (@UserName,@Pwd,@QQ,@eMail)";
SqlParameter[] insParas = {
new SqlParameter("@UserName", SqlDbType.NVarChar) { Value = username },
new SqlParameter("@Pwd", SqlDbType.NVarChar) { Value = pwd },
new SqlParameter("@QQ", SqlDbType.NVarChar) { Value = qq },
new SqlParameter("@eMail", SqlDbType.NVarChar) { Value = email }
};
return SqlHelper.ExecteNonQueryText(insSql, insParas) > 0 ? "註冊成功" : "註冊失敗";
}
}
加密效果對比
- 明文儲存:資料庫直接可見密碼,極度不安全
- 單次 MD5:可透過彩虹表反查
- 雙重 MD5 + 加鹽:
MD5(MD5(pwd) + "salt"),大幅提高破解成本
SQL 注入防護原則
- 永不拼接 SQL 字串,一律使用參數化查詢(
@param + SqlParameter)
- 輸入驗證與長度限制
- 最小權限原則:資料庫帳號僅授予必要權限