iT邦幫忙

2022 iThome 鐵人賽

DAY 12
1
Software Development

C# ASP.NET MVC實作: 30天打造屬於你的網站應用程式系列 第 12

(DAY 12)C# ASP .NET MVC實作: 30天打造屬於你的網站應用程式-授權過濾器驗證

  • 分享至 

  • xImage
  •  

本來今天打算開始進入網站實作範例,不過想先補充講一個東西叫做「授權過濾器」(Authorization Filters),網站實作就延後到明天啦~

「授權過濾器」是屬於「動作過濾器」(Action Filter)的其中一種,動作過濾器可在執行動作方法(Action Method)前後進行一些特殊的邏輯處理,比如:用戶權限驗證、異常處理、系統Log存取等作業。而今天要講的授權過濾器就是使用在用戶權限驗證功能,一般用於會員登入驗證上。

● 範例前置作業

首先比照之前的做法,新建一個ASP. NET Web應用程式(.NET Framework)的專案,專案名稱叫做「DemoAuthorizationFilter」。建立時選擇MVC範本,專案建立後在Controller資料夾內加入新的控制器DemoController。接著在Index()方法內新增一行ViewBag.Name = "陳小明",完成後如下方Code:

namespace DemoAuthorizationFilter.Controllers
{
    public class DemoController : Controller
    {
        // GET: Demo
        public ActionResult Index()
        {
            ViewBag.Name = "陳小明";
            return View();
        }
    }
}

接著新增對應的View,這邊簡單新增如下Code:


@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<div>
    Hello, @ViewBag.Name !!!
</div>

執行結果如下:

● Authorize 屬性

我們在Index()方法上面增加[Authorize],代表這個方法必須先經過授權才能執行,Code如下:

    public class DemoController : Controller
    {
        // GET: Demo
        [Authorize]
        public ActionResult Index()
        {
            ViewBag.Name = "陳小明";
            return View();
        }
    }

重新執行結果會如下圖:

[Authorize]屬性也可放在Controller上方,代表此控制器底下的所有方法都必須先經過授權才能執行,Code示範如下:

    [Authorize]
    public class DemoController : Controller
    {
        // GET: Demo
        public ActionResult Index()
        {
            ViewBag.Name = "陳小明";
            return View();
        }
    }

如果想要限制特定使用者帳號(名稱)才能通過授權的話,也可以在附加在[Authorize]屬性裡,下面的Code範例代表只有使用者名稱為"Alex" 或 "Bonny"才能通過授權執行:

    [Authorize(Users = "Alex,Bonny")]
    public class DemoController : Controller
    {
        // GET: Demo
        public ActionResult Index()
        {
            ViewBag.Name = "陳小明";
            return View();
        }
    }

● AllowAnonymous 屬性

使用[AllowAnonymous]屬性代表此動作方法允許匿名存取,例如當Controller已經設定[Authorize]時,可以在底下的動作方法個別加上[AllowAnonymous]屬性,這樣此方法就能不受授權限制。Code範例如下:

    [Authorize(Users = "Alex,Bonny")]
    public class DemoController : Controller
    {
        // GET: Demo
        public ActionResult Index()
        {
            ViewBag.Name = "陳小明";
            return View();
        }

        [AllowAnonymous]
        public ActionResult Login()
        {
            return View();
        }
    }

上方Code增加了Login()方法並添增[AllowAnonymous]屬性,代表沒有授權的用戶仍可進到登入頁面,這樣顯然比較合理。

● 指定未通過授權時要執行的動作方法

當使用者因為未登入,而被授權過濾器擋下來後,通常會先跳轉到登入頁面讓使用者可以先登入對吧!
想要指定未通過授權時要執行的動作方法,必須先在方案總管底下專案找到Web.Config這個檔案,然後在<system.web>的標籤內新增如下方Code:

    <authentication mode="Forms">
      <forms loginUrl="~/Demo/Login"/>
    </authentication>


這樣代表當未經授權通過時,會執行DemoController底下的Login()方法。

● 範例-會員登入權限驗證

  1. 首先我們先將上面Login()方法的View建立起來,Code如下:

@{
    ViewBag.Title = "Login";
}

<h2>Login</h2>

@using (Html.BeginForm())
{
    <div class="form-group">
        <label>帳號</label>
        <input type="text" name="id" class="form-control" required />
    </div>

    <div class="form-group">
        <label>密碼</label>
        <input type="text" name="password" class="form-control" required />
    </div>

    <div>
        <input type="submit" value="登入" class="btn btn-primary" />
    </div>

    <p class="text-danger">
        @ViewBag.Error
    </p>
}

執行結果如下圖:

  1. 接著新增Login()的POST方法,帶有兩個參數idpassword(還記得是與欄位的name屬性繫結嗎?),同時新增2個Array來當作已儲存的帳密資料。再來利用for迴圈與if判斷輸入的帳密是否存在於現有資料,有的話才能夠成功登入;如果帳密不存在則回傳原本的View,並透過ViewBag攜帶錯誤訊息至View顯示,Code如下:
[Authorize(Users = "Alex,Bonny")]
    public class DemoController : Controller
    {
        // GET: Demo
        public ActionResult Index()
        {
            ViewBag.Name = "陳小明";
            return View();
        }

        [AllowAnonymous]
        public ActionResult Login()
        {
            return View();
        }

        [HttpPost]
        [AllowAnonymous]
        public ActionResult Login(string id, string password)
        {
            var idArray = new string[] { "Alex", "Bonny", "Cody" };
            var pwdArray = new string[] { "111", "222", "333" };

            //檢查輸入帳密是否存在於上面陣列中
            for (int i = 0; i < idArray.Length; i++)
            {
                if (id == idArray[i] && password == pwdArray[i])
                {
                    //表單驗證服務,授權指定帳號
                    FormsAuthentication.RedirectFromLoginPage(id, true);
                    return RedirectToAction("Index");
                }
            }
            ViewBag.Error = "帳密驗證失敗";
            return View();
        }

        public ActionResult Logout()
        {
            FormsAuthentication.SignOut(); //登出
            return RedirectToAction("Login");
        }
    }

這邊要注意在判斷帳密條件為true之後,在return RedirectToAction("Index");之前,使用到一個表單驗證的方法 FormsAuthentication.RedirectFromLoginPage("帳號",是否建立永久Cookie),使用此方法才能讓指定使用者通過驗證,並執行後續頁面導向的行為。FormsAuthentication類別使用時須引用System.Web.Security此命名空間。

  1. 前面已經新增Index()的View了,這邊另外加入一個登出的按鈕,按下後會導至~/Demo/Logout的網址,完整如下Code:

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<div>
    Hello, @ViewBag.Name !!!
</div>
<div>
    <a href="~/Demo/Logout" class="btn btn-default">登出</a>
</div>
  1. 執行~/Demo/Index頁面來看看,會發現自動導向到Login頁面。

當輸入帳密沒有符合條件判斷式時,會提示「帳密驗證失敗」。

輸入其中一組正確帳密(ex:帳號"Alex"、密碼"111")後,即會導向至Index頁面。
但是使用帳號"Cody"與密碼"333"時,會因為前面示範時有加上[Authorize(Users = "Alex,Bonny"),所以並不會驗證成功~

  1. 假如我們希望Index內顯示的是登入使用者的id,可以使用User.Identity.Name來取得目前通過驗證的使用者名稱,所以我們修改一下Code如下:
        public ActionResult Index()
        {
            ViewBag.Name = User.Identity.Name; //取得目前通過驗證的使用者名稱
            return View();
        }

重新執行Index頁面結果如下:

  1. 最後來新增Logout()動作方法,底下使用了FormsAuthentication.SignOut()方法,由瀏覽器移除表單驗證,也就是登出囉~ 登出後就導回登入頁面,Code如下:
        public ActionResult Logout()
        {
            FormsAuthentication.SignOut(); //登出
            return RedirectToAction("Login");
        }

完成後可以再測試看看登出鍵是否可以正確執行,然後可以重新測看看整個流程是否有問題。

● 小結

今天補充說明了關於授權過濾器的一些功能,明天終於可以正式進入網站範例實作啦~那我們就明天見^^


上一篇
(DAY 11)C# ASP .NET MVC實作: 30天打造屬於你的網站應用程式-簡易CRUD
下一篇
(DAY 13)C# ASP .NET MVC實作: 30天打造屬於你的網站應用程式-建立購物中心網站實際範例(一)
系列文
C# ASP.NET MVC實作: 30天打造屬於你的網站應用程式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言