首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> 關於ASP.NET

ASP.NET Forms 驗證實戰 (下):資料庫驗證、Ticket 建立與 Cookie 管理

     編輯:關於ASP.NET

承接 Forms 驗證系列,本文提供完整實戰代碼:SQL Server 使用者表、驗證輔助類、登入頁邏輯、FormsAuthenticationTicket 建立與加密 Cookie 發送、登出與逾期處理。

資料庫準備


CREATE TABLE Users (
    UserId INT IDENTITY PRIMARY KEY,
    UserName NVARCHAR(50) NOT NULL UNIQUE,
    PasswordHash VARBINARY(64) NOT NULL, -- 儲存 SHA256 雜湊
    PasswordSalt VARBINARY(16) NOT NULL,
    Role NVARCHAR(50) NOT NULL,
    IsActive BIT DEFAULT 1
);

驗證輔助類


public static class AuthHelper
{
    private static string ConnStr = ConfigurationManager.ConnectionStrings["AuthDb"].ConnectionString;

    public static bool ValidateUser(string username, string password, out UserInfo user)
    {
        user = null;
        using (var conn = new SqlConnection(ConnStr))
        using (var cmd = new SqlCommand("SELECT UserId, UserName, PasswordHash, PasswordSalt, Role FROM Users WHERE UserName=@u AND IsActive=1", conn))
        {
            cmd.Parameters.AddWithValue("@u", username);
            conn.Open();
            using (var rd = cmd.ExecuteReader())
            {
                if (rd.Read())
                {
                    byte[] hash = (byte[])rd["PasswordHash"];
                    byte[] salt = (byte[])rd["PasswordSalt"];
                    byte[] inputHash = HashPassword(password, salt);
                    if (StructuralComparisons.StructuralEqualityComparer.Equals(hash, inputHash))
                    {
                        user = new UserInfo
                        {
                            UserId = rd.GetInt32(0),
                            UserName = rd.GetString(1),
                            Role = rd.GetString(4)
                        };
                        return true;
                    }
                }
            }
        }
        return false;
    }

    private static byte[] HashPassword(string pwd, byte[] salt)
    {
        using (var sha = SHA256.Create())
        {
            byte[] pwdBytes = Encoding.UTF8.GetBytes(pwd);
            byte[] combined = new byte[salt.Length + pwdBytes.Length];
            Buffer.BlockCopy(salt, 0, combined, 0, salt.Length);
            Buffer.BlockCopy(pwdBytes, 0, combined, salt.Length, pwdBytes.Length);
            return sha.ComputeHash(combined);
        }
    }
}

public class UserInfo { public int UserId { get; set; } public string UserName { get; set; } public string Role { get; set; } }

登入頁代碼 (Login.aspx.cs)


protected void btnLogin_Click(object sender, EventArgs e)
{
    if (AuthHelper.ValidateUser(txtUserName.Text, txtPassword.Text, out UserInfo user))
    {
        // 建立 Ticket:UserData 存放 UserId|Role
        string userData = $"{user.UserId}|{user.Role}";
        var ticket = new FormsAuthenticationTicket(
            version: 1,
            name: user.UserName,
            issueDate: DateTime.Now,
            expiration: DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes),
            isPersistent: chkRememberMe.Checked,
            userData: userData,
            cookiePath: FormsAuthentication.FormsCookiePath);

        // 加密並寫入 Cookie
        string encTicket = FormsAuthentication.Encrypt(ticket);
        var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket)
        {
            HttpOnly = true,
            Secure = FormsAuthentication.RequireSSL,
            Expires = ticket.IsPersistent ? ticket.Expiration : DateTime.MinValue
        };
        Response.Cookies.Add(cookie);

        // 導向原請求頁面或預設頁
        string returnUrl = Request.QueryString["ReturnUrl"];
        Response.Redirect(string.IsNullOrEmpty(returnUrl) ? "Default.aspx" : returnUrl);
    }
    else
    {
        lblError.Text = "帳號或密碼錯誤";
    }
}

Global.asax 解析 Ticket 並建立 Principal


protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    if (HttpContext.Current.User?.Identity is FormsIdentity fid)
    {
        var ticket = fid.Ticket;
        string[] parts = ticket.UserData.Split('|');
        int userId = int.Parse(parts[0]);
        string role = parts[1];

        // 可選:從快取/資料庫載入完整權限
        string[] permissions = PermissionService.GetPermissions(userId);

        HttpContext.Current.User = new CustomPrincipal(fid, new[] { role }, userId, permissions);
        Thread.CurrentPrincipal = HttpContext.Current.User;
    }
}

登出與逾期


// 登出按鈕
protected void btnLogout_Click(object sender, EventArgs e)
{
    FormsAuthentication.SignOut(); // 清除 Cookie
    Response.Redirect("Login.aspx");
}

// Web.config 設定逾期 (滑動逾期)
<forms loginUrl="Login.aspx" timeout="30" slidingExpiration="true" protection="All" />

安全性強化建議

  1. 密碼儲存使用 PBKDF2/BCrypt/Argon2,勿用單純 SHA256。
  2. 啟用 RequireSSL="true" 強制 HTTPS 傳輸 Cookie。
  3. HttpOnly="true" 防止 XSS 竊取 Cookie。
  4. 實作暴力破解防護:失敗計數、帳號鎖定、驗證碼。
  5. 定期輪換 machineKey (驗證/加密金鑰)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved