這是前一陣子在改善未登入者的跳轉方式的衍生問題。之前的方法都是考慮沒有登入的使用者,要前往不同身分才能瀏覽的頁面時分別導向不同的登入畫面。
今天的體驗則是再加強一下。如果你已經登入了,要前往不同身分才能使用的頁面。是不是也要重新登入?
我們之前做的是 Authentication
身分驗證,就像是大門的警衛,有員工證的人才能進公司。
當你有了員工證,進了公司後,你想要進董事長辦公室。能進不能進,就看你有沒有權限,這就是Authorization
授權驗證。
在 ASP.NET MVC 裡面可以使用 AuthorizeAttribute
來做不同身分的授權驗證。
使用的方式很簡單。
[Authorize(Roles = "teacher")]
public IActionResult Index()
{
...
在 MVC 的生命週期中,會先經過AuthenticationFilter
(身分驗證)->AuthorizeFilter
(授權驗證)->ActionFilter
但如果你的權限規則不一樣?判斷邏輯很特殊?那你可以自己實作一個過濾器AuthorizeFilter
來處理權限判斷。管理權限的就是AuthorizeAttribute
。所以實作最簡單的方式就是繼承AuthorizeAttribute
後,覆寫他。
在 MVC 的生命週期中,會先經過AuthenticationFilter
(身分驗證)->AuthorizeFilter
(授權驗證)->ActionFilter
最簡單的方式就是繼承AuthorizeAttribute
後,覆寫裡面負責權限判斷的AuthorizeCore()
就可以了。
public class TeatherAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var user = httpContext.User as ClaimsPrincipal;
// 是不是老師
return user.HasClaim("MyRole", "老師");
}
}
AuthorizeCore
只是負責判斷 True 或 False 而已。
可以看一下AuthorizeAttribute
的OnAuthorization()
內容,以下是程式碼原始的內容。
/// <summary>在處理序要求授權時呼叫。</summary>
/// <param name="filterContext">篩選條件內容,這個內容封裝 <seecref="T:System.Web.Mvc.AuthorizeAttribute" /> 的使用資訊。</param>
/// <exception cref="T:System.ArgumentNullException">
/// <paramref name="filterContext" /> 參數是 null。</exception>
public virtual void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException(nameof (filterContext));
if (OutputCacheAttribute.IsChildActionCacheActive((ControllerContext) filterContext))
throw new InvalidOperationException(MvcResources.AuthorizeAttribute_CannotUseWithinChildActionCache);
if ((filterContext.ActionDescriptor.IsDefined(typeof (AllowAnonymousAttribute), true) ? 1 : (filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof (AllowAnonymousAttribute), true) ? 1 : 0)) != 0)
return;
if (this.AuthorizeCore(filterContext.HttpContext))
{
HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
cache.SetProxyMaxAge(new TimeSpan(0L));
cache.AddValidationCallback(new HttpCacheValidateHandler(this.CacheValidateHandler), (object) null);
}
else
this.HandleUnauthorizedRequest(filterContext);
}
可以看到OnAuthorization()
,會判斷AllowAnonymousAttribute
後,才會執行AuthorizeCore()
拿到驗證結果,如果驗證不通過,會去執行HandleUnauthorizedRequest()
,所以我們接下來就覆寫 HandleUnauthorizedRequest()
,來達到我們跳轉的需求
HandleUnauthorizedRequest
原始的程式碼如下:
/// <summary>處理授權失敗的 HTTP 要求。</summary>
/// <param name="filterContext">封裝 <see cref="T:System.Web.Mvc.AuthorizeAttribute" /> 的使用資訊。<paramref name="filterContext" /> 物件,這個物件包含控制器、HTTP 內容、要求內容、動作結果和路由資料。</param>
protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext) => filterContext.Result = (ActionResult) new HttpUnauthorizedResult();
我簡單改寫後如下
public class TeatherAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var user = httpContext.User as ClaimsPrincipal;
// 是不是老師
return user.HasClaim("MyRole", "老師");
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Controller.TempData["ErrorMsg"] = "Fail|訪問此頁面需要老師權限,請重新登入!";
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new { action = "login", controller = "Teather" }));
}
}