本來今天打算開始進入網站實作範例,不過想先補充講一個東西叫做「授權過濾器」(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>
執行結果如下:
我們在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]
屬性代表此動作方法允許匿名存取,例如當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()
方法。
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>
}
執行結果如下圖:
Login()
的POST方法,帶有兩個參數id
與password
(還記得是與欄位的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
此命名空間。
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>
~/Demo/Index
頁面來看看,會發現自動導向到Login頁面。當輸入帳密沒有符合條件判斷式時,會提示「帳密驗證失敗」。
輸入其中一組正確帳密(ex:帳號"Alex"、密碼"111")後,即會導向至Index頁面。
但是使用帳號"Cody"與密碼"333"時,會因為前面示範時有加上[Authorize(Users = "Alex,Bonny")
,所以並不會驗證成功~
id
,可以使用User.Identity.Name
來取得目前通過驗證的使用者名稱,所以我們修改一下Code如下: public ActionResult Index()
{
ViewBag.Name = User.Identity.Name; //取得目前通過驗證的使用者名稱
return View();
}
重新執行Index頁面結果如下:
Logout()
動作方法,底下使用了FormsAuthentication.SignOut()
方法,由瀏覽器移除表單驗證,也就是登出囉~ 登出後就導回登入頁面,Code如下: public ActionResult Logout()
{
FormsAuthentication.SignOut(); //登出
return RedirectToAction("Login");
}
完成後可以再測試看看登出鍵是否可以正確執行,然後可以重新測看看整個流程是否有問題。
今天補充說明了關於授權過濾器的一些功能,明天終於可以正式進入網站範例實作啦~那我們就明天見^^