<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Login.aspx.cs" Inherits="Login" %>
<!DOCTYPE html>
<html><head><title>使用者登入</title></head>
<body>
<form id="form1" runat="server">
<div style="max-width:400px;margin:50px auto;padding:20px;border:1px solid #ddd;border-radius:4px;">
<h2>登入系統</h2>
<div style="margin-bottom:15px;">
<asp:Label ID="lblUser" runat="server" Text="使用者帳號:" AssociatedControlID="txtUser" CssClass="form-label" /><br />
<asp:TextBox ID="txtUser" runat="server" CssClass="form-control" />
<asp:RequiredFieldValidator ID="rfvUser" runat="server" ControlToValidate="txtUser"
ErrorMessage="帳號不可空白" Display="Dynamic" CssClass="text-danger" />
</div>
<div style="margin-bottom:15px;">
<asp:Label ID="lblPwd" runat="server" Text="密碼:" AssociatedControlID="txtPwd" CssClass="form-label" /><br />
<asp:TextBox ID="txtPwd" runat="server" TextMode="Password" CssClass="form-control" />
<asp:RequiredFieldValidator ID="rfvPwd" runat="server" ControlToValidate="txtPwd"
ErrorMessage="密碼不可空白" Display="Dynamic" CssClass="text-danger" />
</div>
<div style="margin-bottom:15px;">
<asp:CheckBox ID="chkRemember" runat="server" Text="記住我 (持久性 Cookie)" />
</div>
<asp:Button ID="btnLogin" runat="server" Text="登入" CssClass="btn btn-primary" OnClick="btnLogin_Click" ValidationGroup="Login" />
<asp:ValidationSummary ID="vsLogin" runat="server" ValidationGroup="Login" HeaderText="請修正以下錯誤:" CssClass="alert alert-danger" />
</div>
</form>
</body></html>
using System;
using System.Data.SqlClient;
using System.Web.Security;
using System.Web.UI;
public partial class Login : Page
{
protected void btnLogin_Click(object sender, EventArgs e)
{
if (!Page.IsValid) return;
string user = txtUser.Text.Trim();
string pwd = txtPwd.Text;
// 1. 驗證帳密 (範例簡化,實務應用雜湊比對)
if (ValidateUser(user, pwd))
{
// 2. 取得角色 (實務從 DB 讀取)
string roles = GetUserRoles(user); // 如 "Admin,Editor"
// 3. 建立驗證票據
var ticket = new FormsAuthenticationTicket(
1, user, DateTime.Now, DateTime.Now.AddMinutes(30),
chkRemember.Checked, roles, FormsAuthentication.FormsCookiePath);
// 4. 加密寫入 Cookie
string encTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket)
{
HttpOnly = true,
Secure = FormsAuthentication.RequireSSL
};
if (chkRemember.Checked) cookie.Expires = ticket.Expiration;
Response.Cookies.Add(cookie);
// 5. 導向
Response.Redirect(FormsAuthentication.GetRedirectUrl(user, chkRemember.Checked));
}
else
{
lblError.Text = "帳號或密碼錯誤";
}
}
private bool ValidateUser(string username, string password)
{
// 實務:參數化查詢 + 密碼雜湊驗證 (BCrypt/PBKDF2)
string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["MyDb"].ConnectionString;
using (var conn = new SqlConnection(connStr))
using (var cmd = new SqlCommand("SELECT PasswordHash, PasswordSalt FROM Users WHERE UserName=@User", conn))
{
cmd.Parameters.AddWithValue("@User", username);
conn.Open();
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
{
string hash = reader.GetString(0);
string salt = reader.GetString(1);
return HashHelper.Verify(password, hash, salt); // 自訂驗證方法
}
}
}
return false;
}
private string GetUserRoles(string username)
{
// 從 DB 讀取角色字串
return "User"; // 簡化
}
}
<configuration>
<connectionStrings>
<add name="MyDb" connectionString="Server=.;Database=WebSolution;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
<!-- 1. 啟用表單驗證 -->
<authentication mode="Forms">
<forms name=".AUTHCOOKIE"
loginUrl="Login.aspx"
timeout="30"
path="/"
protection="All"
requireSSL="false"
slidingExpiration="true" />
</authentication>
<!-- 2. 預設授權規則:拒絕匿名 -->
<authorization>
<deny users="?" />
</authorization>
<!-- 3. 公開頁面允許匿名 -->
<location path="Login.aspx">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
<location path="Register.aspx">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
<!-- 4. 管理區域僅限 Admin 角色 -->
<location path="Admin">
<system.web>
<authorization>
<allow roles="Admin" />
<deny users="*" />
</authorization>
</system.web>
</location>
</system.web>
</configuration>
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie == null) return;
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(authCookie.Value);
if (ticket == null || ticket.Expired) return;
string[] roles = ticket.UserData.Split(',', StringSplitOptions.RemoveEmptyEntries);
FormsIdentity identity = new FormsIdentity(ticket);
GenericPrincipal principal = new GenericPrincipal(identity, roles);
Context.User = principal;
}
<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
if (User.IsInRole("Admin"))
{
pnlAdmin.Visible = true;
}
lblUser.Text = $"歡迎, {User.Identity.Name} (角色: {string.Join(",", ((System.Security.Principal.GenericPrincipal)User).Roles)})";
}
</script>
<html><body>
<asp:Label ID="lblUser" runat="server" /><br />
<asp:Panel ID="pnlAdmin" runat="server" Visible="false">管理功能區塊</asp:Panel>
</body></html>
| 設定 | 用途 |
|---|---|
| forms name | Cookie 名稱,避免多應用程式衝突 |
| loginUrl | 未驗證導向頁面 |
| timeout | 票據有效分鐘數 (slidingExpiration=true 時為滑動過期) |
| protection="All" | 加密 + 驗證 (防竄改) |
| requireSSL | 生產環境必須 true,確保 Cookie 只在 HTTPS 傳輸 |
| authorization deny users="?" | 預設拒絕匿名,實現「預設拒絕」安全原則 |
| location path | 針對特定路徑覆寫授權規則 |
ASP.NET 表單驗證授權完整鏈路:登入頁驗證 → `FormsAuthenticationTicket` 含角色 → 加密 Cookie → `Global.asax` 還原 `GenericPrincipal` → `web.config` `