ASP.NET Forms Authentication 透過 **加密 Cookie** (`FormsAuthenticationTicket`) 維持使用者登入狀態。核心流程:
<configuration>
<system.web>
<authentication mode="Forms">
<forms
loginUrl="Login.aspx"
name=".ASPXAUTH"
timeout="30"
path="/"
protection="All"
requireSSL="false"
slidingExpiration="true" />
</authentication>
<authorization>
<deny users="?" /> <!-- 拒絕匿名 -->
</authorization>
</system.web>
</configuration>
關鍵屬性:`slidingExpiration="true"` 活動延長期限;`protection="All"` 加密+簽章;`requireSSL="true"` 生產環境必開。
<%@ Page Language="C#" %>
<script runat="server">
protected void btnLogin_Click(object sender, EventArgs e)
{
// 實務應查詢資料庫、驗證密碼雜湊 (BCrypt/PBKDF2)
if (ValidateUser(txtUser.Text, txtPass.Text))
{
// 建立票據:使用者名稱、是否持久、逾時分鐘
var ticket = new FormsAuthenticationTicket(
1, txtUser.Text, DateTime.Now, DateTime.Now.AddMinutes(30),
chkPersist.Checked, "Admin,Editor", FormsAuthentication.FormsCookiePath);
string encTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket)
{
HttpOnly = true,
Secure = FormsAuthentication.RequireSSL,
Path = FormsAuthentication.FormsCookiePath
};
if (chkPersist.Checked) cookie.Expires = ticket.Expiration;
Response.Cookies.Add(cookie);
// 導向原請求頁或預設頁
Response.Redirect(FormsAuthentication.GetRedirectUrl(txtUser.Text, chkPersist.Checked));
}
else
{
lblError.Text = "帳號或密碼錯誤";
}
}
private bool ValidateUser(string user, string pass)
{
// 示範:實際應使用參數化查詢 + 密碼雜湊驗證
return user == "admin" && pass == "password123";
}
</script>
<html><body>
<form runat="server">
帳號: <asp:TextBox ID="txtUser" runat="server" /><br/>
密碼: <asp:TextBox ID="txtPass" TextMode="Password" runat="server" /><br/>
<asp:CheckBox ID="chkPersist" runat="server" Text="記住我" /><br/>
<asp:Button ID="btnLogin" Text="登入" OnClick="btnLogin_Click" runat="server" />
<asp:Label ID="lblError" ForeColor="Red" runat="server" />
</form></body></html>
<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
lblWelcome.Text = $"歡迎, {Context.User.Identity.Name} | 角色: {((FormsIdentity)Context.User.Identity).Ticket.UserData}";
}
protected void btnLogout_Click(object sender, EventArgs e)
{
FormsAuthentication.SignOut();
Response.Redirect("Login.aspx");
}
</script>
<html><body>
<asp:Label ID="lblWelcome" runat="server" /><br/>
<asp:Button ID="btnLogout" Text="登出" OnClick="btnLogout_Click" runat="server" />
</body></html>
// Ticket 建立時夾帶角色字串 (逗號分隔)
var ticket = new FormsAuthenticationTicket(..., "Admin,Editor", ...);
// Global.asax 解析角色填入 Principal
protected void Application_AuthenticateRequest()
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] is HttpCookie cookie)
{
var ticket = FormsAuthentication.Decrypt(cookie.Value);
if (ticket != null)
{
var roles = ticket.UserData.Split(',');
var identity = new FormsIdentity(ticket);
var principal = new System.Security.Principal.GenericPrincipal(identity, roles);
Context.User = principal;
}
}
}
// 頁面/類別層級授權
// web.config
// 程式碼檢查
if (User.IsInRole("Admin")) { ... }
// URL 變為 /(F(EncryptedTicket))/Login.aspx
// 注意:URL 洩露風險高,僅建議無 Cookie 環境使用
多台伺服器**必須共用相同 machineKey**,否則 Ticket 解密失敗導致使用者頻繁被登出。
新專案請直接使用 **ASP.NET Core Identity**:內建 EF Core 整合、雙因子驗證、外部登入、Token 刷新、GDPR 合規,架構更現代且長期支援。
Forms Authentication 雖屬經典技術,理解其 Ticket/Cookie/Module 運作原理,對維護舊系統、設計自訂驗證機制、轉型現代架構皆有幫助。