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

ASP.NET MVC ActionFilter 實作統一授權機制:從分散邏輯到集中攔截

     編輯:關於ASP.NET

在 MVC 應用中,URL 直接對應 Action 方法,授權檢查若分散寫在每個 Action 內,不僅違反 DRY 原則,更難維護。本文示範如何透過 ActionFilter 實作集中式授權攔截,將授權邏輯從 Action 移至 Filter,達成關注點分離。

問題場景:分散式授權的痛點

假設 BookController 有 Index(int id) Action,需驗證用戶是否擁有該書籍存取權:

public ActionResult Index(int id)
{
    if (new AuthorizeService().IsBookAuthorized(id, User.Identity.Name))
    {
        var book = new BookService().GetBook(id);
        return View(book);
    }
    return View("NotAuthorized");
}

缺點:

  1. 授權邏輯散落各 Action,重複程式碼多。
  2. 單元測試需模擬授權服務,增加複雜度。
  3. 新增授權規則需修改所有相關 Action。
  4. 授權發生在 Action 內部,無法在更早管線階段攔截。

解決方案:自訂 AuthorizeAttribute

繼承 FilterAttribute、IAuthorizationFilter,在 OnAuthorization 方法中實作邏輯:

using System;
using System.Web.Mvc;

namespace MyProject.Filters
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
    public class BookAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null) throw new ArgumentNullException(nameof(filterContext));

            // 1. 取得路由參數 bookId
            var routeData = filterContext.RouteData;
            var bookIdStr = routeData.Values["id"]?.ToString();
            if (!int.TryParse(bookIdStr, out int bookId))
            {
                HandleUnauthorizedRequest(filterContext, "無效的書籍識別碼。");
                return;
            }

            // 2. 取得當前用戶身分
            var userName = filterContext.HttpContext.User.Identity.Name;
            if (string.IsNullOrEmpty(userName))
            {
                HandleUnauthorizedRequest(filterContext, "請先登入。");
                return;
            }

            // 3. 呼叫授權服務(建議透過 DI 注入)
            var authService = DependencyResolver.Current.GetService<IAuthorizeService>();
            if (authService == null || !authService.IsBookAuthorized(bookId, userName))
            {
                HandleUnauthorizedRequest(filterContext, "您無權存取此書籍。");
                return;
            }

            // 授權通過,繼續執行 Action
        }

        private void HandleUnauthorizedRequest(AuthorizationContext filterContext, string message)
        {
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                filterContext.Result = new JsonResult
                {
                    Data = new { success = false, message },
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
                };
            }
            else
            {
                filterContext.Result = new ViewResult
                {
                    ViewName = "NotAuthorized",
                    ViewData = new ViewDataDictionary<string>(message)
                };
            }
        }
    }
}

套用於 Controller 或 Action

[BookAuthorize]
public class BookController : Controller
{
    public ActionResult Index(int id) { ... }

    [BookAuthorize] // 可覆蓋或疊加
    public ActionResult Edit(int id) { ... }
}

進階:整合 Dependency Injection

避免在 Filter 中直接 new AuthorizeService(),透過 IDependencyResolver 或 ASP.NET Core 內建 DI 取得服務實例:

// ASP.NET MVC 5 使用 Unity/Autofac 等容器註冊
container.RegisterType<IAuthorizeService, AuthorizeService>();

// Filter 中解析
var authService = filterContext.HttpContext.Request.RequestContext
    .HttpContext.ApplicationInstance
    .Container.Resolve<IAuthorizeService>();

與內建 [Authorize] 比較

特性[Authorize]自訂 BookAuthorizeAttribute
角色/用戶名稱驗證支援需自行實作
資源級授權 (Resource-based)不支援支援(依路由參數)
自訂拒絕回應導向登入頁可回傳 JSON、View、403
DI 整合需自訂 AuthorizeAttribute原生支援

最佳實踐建議

  1. 建立基底 ResourceAuthorizeAttribute<TResource> 泛型類別,減少重複程式碼。
  2. 授權服務介面設計為 bool IsAuthorized(string userName, TResource resource),支援單元測試。
  3. 全域註冊:GlobalFilters.Filters.Add(new BookAuthorizeAttribute()); 配合 [AllowAnonymous] 例外。
  4. 記錄授權失敗日誌,便於稽核與偵錯。

結語

ActionFilter 是 MVC 管線中的強大攔截點。將橫切關注點(授權、日誌、快取、例外處理)封裝為 Filter,能讓 Action 專注於業務邏輯,符合單一職責原則。下一篇將探討 ActionMethodSelectorAttribute 實現多角色對應不同 View 的進階選擇機制。

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