如何利用Filter在ASP.NET MVC專案中設計權限管理

作者:黃家瑞
精誠資訊 恆逸教育訓練中心 資深講師
技術分類:程式設計

實務上,在一個ASP.NET MVC系統中,使用者第一個接觸到的介面往往就是登入介面。


登入系統確認使用者身分後,系統的工作才正要開始。確認使用者有權限可以進入系統,便需要在Control的每個Action中去確認,該使用者是否有權限進入這個功能項目,例如在系統中有一個建立基本資料的 BasicDataController

public class BasicDataController : Controller
{
    // 產品列表
    public ActionResult ProductList
    {
        // 如果不是合法的使用者,轉回登入頁面
        if (!Request.IsAuthenticated) { 
                FormsAuthentication.RedirectToLoginPage();  
        // 產品列表的程式碼….
        return new View();
    }
    // 新增產品
    public ActionResult CreateProduct
    {
        // 如果不是合法的使用者,轉回登入頁面
        if (!Request.IsAuthenticated) { 
                FormsAuthentication.RedirectToLoginPage();  
        // 新增產品的程式碼….
        return new View();
    }
    // 編輯產品
    public ActionResult EditProduct
    {
        // 如果不是合法的使用者,轉回登入頁面
        if (!Request.IsAuthenticated) { 
                FormsAuthentication.RedirectToLoginPage();  
        // 編輯產品的程式碼….
        return new View();
    }

幾乎在每個Action上都要做相同的事情

// 如果不是合法的使用者,轉回登入頁面
if (!Request.IsAuthenticated) { 
    FormsAuthentication.RedirectToLoginPage();  

這樣子的系統,除了要重複相當多的程式碼外,當權限管理更複雜時(要依使用者身分來區別是否可以進入該功能)程式會變得難以維護。利用MVC一個相當方便的功能Authorization Filters,可以解決這個問題。(在此要提醒,安全性機制要考量的問題相當廣,只是單純將程式複製貼上,或可以解決眼前的問題,但是當系統放上真實世界的網路時,面對各種安全性漏洞,必須做更嚴密的測試,在這裡僅就ASP.NET MVC專案中的 Authorization Filters做討論,至於資訊安全問題是另一個層次的議題了,在此不就這個問題討論)

建立一個MVC專案


新增HomeController


加入Index, ProductList, ProductCreate, ProductEdit, ProductDelete等5個Action以及View

Index.cshtml

<div> <h1>Index</h1> @Html.ActionLink("產品列表", "ProductList"); </div>

ProductList.cshtml

<div> <h1>Index</h1> @Html.ActionLink("產品列表", "ProductList"); </div>

ProductAdd.cshtml

<div> <h1>Index</h1> @Html.ActionLink("新增產品", "ProductAdd"); </div>

ProductEdit.cshtml

<div> <h1>Index</h1> @Html.ActionLink("編輯產品", "ProductEdit"); </div>

ProductDelete.cshtml

<div> <h1>Index</h1> @Html.ActionLink("刪除產品", "ProductDelete"); </div>

新增一個Infrastructure資料夾,並且在Infrustrator中加入CustomAuthAttribute類別


注意:請以Attribute做為結尾。

CustomAuthAttribut.cs內容如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Inventory.Infrastructure
{
    public class CustomAuthAttribute:AuthorizeAttribute
    {
       private string _authstr;
       public CustomAuthAttribute(string attrib)
       {
           _authstr = attrib;
       }
      protected override bool AuthorizeCore(HttpContextBase httpContext)
       {
           if (httpContext.Request.IsLocal)
               return _authstr=="YES";
           else
               return true;
       }
    }
}

1.加入命名空間 System.Web.Mvc

using System.Web.Mvc;

2.繼承AuthorizeAttribute

namespace Inventory.Infrastructure
{
    public class CustomAuthAttribute:AuthorizeAttribute
    {

參數由建構函式傳遞至Filter內,覆載AuthorizeCore驗證不通過時擲出意外

       private string _authstr;
       public CustomAuthAttribute(string attrib)
       {
           _authstr = attrib;
       }
      protected override bool AuthorizeCore(HttpContextBase httpContext)
       {
           if (httpContext.Request.IsLocal)
               return _authstr=="YES";
           else
               return true;
       }

再回到HomeController中

using Inventory.Infrastructure;
....
        [CustomAuth("YES")]
        public ActionResult ProductList()
        {
            return View();
        }
        [CustomAuth("NO")]
        public ActionResult ProductAdd()
        {
            return View();
        }
        [CustomAuth("NO")]
        public ActionResult ProductEdit()
        {
            return View();
        }
        [CustomAuth("NO")]
        public ActionResult ProductDelete()
        {
            return View();
        }

3.加入Inventory..Infrastructure命名空間(撰寫Filter的資料夾)

    using Inventory.Infrastructure;

4.在要驗證的Action前面加上Attribute

        [CustomAuth("YES")]
        public ActionResult ProductList()
        {
            return View();
        }
        [CustomAuth("NO")]
        public ActionResult ProductAdd()
        {
            return View();
        }

[CustomAuth(“YES”)]會將”YES”由new CustomAuthAttribute(“Yes”)傳遞至建構函式中,藉由AuthorizeCore方法驗證,當回傳false時,則會擲出意外。
以下示範:


按下產品列表

[CustomAuth("YES")]
public ActionResult ProductList()

點選新增產品

[CustomAuth("NO")]
public ActionResult ProducAdd()

產生401(沒有權限)的錯誤

再回到CustomAuthAttribute.cs中加入

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
    filterContext.Result = new RedirectResult("/Home/Index");
}

將錯誤的頁面轉到 Home/Index(或是Login)即可完成這部份的自定驗證權限工作。