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

ASP.NET Session 過期檢測三大方案深度比較:Page_Load vs HttpModule vs 基底頁面類別

     編輯:ASP.NET 基礎

背景:為何需要統一 Session 檢測?

在每個頁面的 `Page_Load` 重複撰寫 `if (Session["User"] == null) Response.Redirect("Login.aspx")` 不僅違反 DRY 原則,還易漏寫導致安全漏洞。本文比較三種集中化方案優劣。

方案一:基底頁面類別重寫 OnInit(推薦:彈性最佳)

實作


public class BasePage : System.Web.UI.Page
{
    protected override void OnInit(EventArgs e)
    {
        base.OnInit(e);
        
        // 排除登入頁、公開頁面
        string[] publicPages = { "/Login.aspx", "/Register.aspx", "/Public/" };
        string rawUrl = Request.RawUrl.ToLower();
        
        if (!publicPages.Any(p => rawUrl.StartsWith(p)))
        {
            if (Session["UserID"] == null)
            {
                // AJAX 請求回傳 JSON,一般請求導向登入頁
                if (Request.Headers["X-Requested-With"] == "XMLHttpRequest")
                {
                    Response.StatusCode = 401;
                    Response.ContentType = "application/json";
                    Response.Write("{\"error\":\"session_expired\"}");
                    Response.End();
                }
                else
                {
                    Response.Redirect($"~/Login.aspx?ReturnUrl={Server.UrlEncode(Request.RawUrl)}");
                }
            }
        }
    }
}

使用方式


// 頁面繼承 BasePage 而非 Page
public partial class Admin_Default : BasePage { ... }

優缺點

優點缺點
彈性高:可逐頁決定是否繼承現有專案需逐頁修改繼承關係
邏輯集中,易維護無法攔截非 .aspx 請求(如 .ashx、.asmx、靜態檔)
可存取完整 Page 生命週期事件
支援 AJAX/一般請求差異化處理

方案二:HttpModule 全域攔截(推薦:最徹底、效能最佳)

實作


public class SessionCheckModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        // AcquireRequestState 之後 Session 才可用
        context.AcquireRequestState += OnAcquireRequestState;
    }

    private void OnAcquireRequestState(object sender, EventArgs e)
    {
        var app = (HttpApplication)sender;
        var ctx = app.Context;
        
        // 排除公開路徑
        if (IsPublicPath(ctx.Request.Path)) return;
        
        // 檢查 Session
        if (ctx.Session["UserID"] == null)
        {
            if (IsAjaxRequest(ctx.Request))
            {
                ctx.Response.StatusCode = 401;
                ctx.Response.ContentType = "application/json";
                ctx.Response.Write("{\"error\":\"session_expired\"}");
            }
            else
            {
                ctx.Response.Redirect($"~/Login.aspx?ReturnUrl={ctx.Server.UrlEncode(ctx.Request.RawUrl)}");
            }
            ctx.Response.End(); // 短路管線
        }
    }

    private bool IsPublicPath(string path) 
        => path.StartsWith("/Login.aspx", StringComparison.OrdinalIgnoreCase) 
        || path.StartsWith("/Public/", StringComparison.OrdinalIgnoreCase)
        || path.EndsWith(".axd", StringComparison.OrdinalIgnoreCase); // 排除 WebResource.axd 等

    private bool IsAjaxRequest(HttpRequest req) 
        => req.Headers["X-Requested-With"] == "XMLHttpRequest";

    public void Dispose() { }
}

註冊 (IIS 7+ 整合模式)


<system.webServer>
  <modules>
    <add name="SessionCheckModule" type="MyNamespace.SessionCheckModule, MyAssembly" preCondition="managedHandler" />
  </modules>
</system.webServer>

優缺點

優點缺點
一次配置,全站生效(含 .ashx、.asmx、Controller)較早攔截,Session 可能尚未建立(需在 AcquireRequestState 之後)
效能最佳:在管線早期短路,不執行後續 Handler邏輯較隱蔽,新人難發現
可攔截所有 managed 請求靜態檔案 (.jpg, .css) 預設不經 managed 管線,除非設定 runAllManagedModulesForAllRequests

方案三:每頁 Page_Load 判斷(不推薦)


protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack && Session["UserID"] == null)
        Response.Redirect("Login.aspx");
}

缺點明顯:程式碼冗餘、易漏寫、難維護、無法處理 AJAX、違反關注點分離。

綜合比較與選型建議

維度BasePageHttpModulePage_Load
覆蓋範圍.aspx 頁面所有 managed 請求單一頁面
遷移成本中(逐頁改繼承)低(僅註冊模組)高(每頁加碼)
彈性高(可選擇性繼承)中(靠路徑排除)低
效能良最佳(早期短路)差
建議場景WebForms 專案、需頁面級控制新專案、MVC 混合、追求徹底極小專案、快速原型

進階技巧:滑動過期延長

在 `Global.asax` 或 `BasePage.OnInit` 中:


// 每次請求重置 Session 過期計時(滑動過期)
Session.Timeout = 20; // 分鐘,覆蓋 web.config 設定

總結

新專案首選 HttpModule,徹底解決全站驗證;既有 WebForms 專案 採用 BasePage 平滑遷移;拒絕在 Page_Load 重複判斷。配合 AJAX 差異化回應,提升使用者體驗與安全性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved