.Net MVC本身就提供一套AuthorizeAttribute
但它常常與現有系統驗證、或是資料庫內的現有帳號資料表無法整合
所以今天與大家分享怎麼實作自己的AuthorizeAttribute
在MVC預設的AuthorizeAttribute中
你可以把Users理解成帳號
Roles理解成群組
有時後只想讓某些人進的去一個頁面、有時後希望根據群組進行權限設定
而假設我們是一間公司
通常以內部網站來說,帳號=網域名稱、群組=部門
另外
假設除了原生AuthorizeAttribute帶給我們的 Users 跟 Roles 以外
我們還需要多增加一個布林值欄位叫 IsPageAdminOnly = 是否此頁面只有管理者允許進入
而我們最後希望在Controller中呼叫的方式如下
所以我們會有一個現有的帳號資料表
假設結構如下
再跟大家提醒一次
Users = AD_ACCOUNT
Roles = Department
接下來我們會繼承原生的AuthorizeAttribute 假設我把新類別取名為PermissionFilter
而通常我們會開一個Filters資料夾來存放這個檔案
而PermissionFilter的完整程式碼如下
using Repository.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace SPIL.B2B.Portal.Filters
{
public class PermissionFilter : AuthorizeAttribute
{
private static readonly string _unAuthUrl = @"..\Error\Unauthorized401";
private static readonly string _forbiddenUrl = @"..\Error\Forbidden403";
//有些頁面也許只想讓admin進入
public bool IsPageAdminOnly = false;
public override void OnAuthorization(AuthorizationContext filterContext)
{
//對有進網域的電腦而言不太可能發生
if (filterContext == null)
{
filterContext.Result = new RedirectResult(_unAuthUrl);
}
if (AuthorizeCore(filterContext.HttpContext))
{
//驗證有過也不留cache
SetCachePolicy(filterContext);
}
else if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
//通常 AuthorizeCore 沒過都是 403
filterContext.Result = new RedirectResult(_forbiddenUrl);
}
else
{
//401 (對有進網域的電腦而言不太可能發生)
filterContext.Result = new RedirectResult(_unAuthUrl);
}
}
private void SetCachePolicy(AuthorizationContext filterContext)
{
//怕下一秒把這個人被改成Unauth,但因為上一秒他成功進來過,被瀏覽器cache permission,導致雖然已unauth卻還是進的來,所以set 0
var cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidateHandler, null);
}
private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var adAccount = httpContext.User.Identity.Name;
//是不是admin only
if (IsPageAdminOnly && !HasAdminAuth(adAccount))
{
return false;
}
else
{
//其他用個人AD和部門權限判斷
return HasAuth(adAccount);
}
}
private bool HasAdminAuth(string adAccount)
{
//為了方便講解--------------------------------------------------------
var admins = new List<string> { @"MyCompany\John" };
//--------------------------------------------------------------------
return admins.Contains(adAccount) ? true : false;
}
private bool HasAuth(string adAccount)
{
//取得允許名單
var allowUsers = Users.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var allowRoles = Roles.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (allowUsers.Any() && !allowUsers.Contains(adAccount))
{
return false;
}
if (allowRoles.Any() && !HasRoleAuth(adAccount, allowRoles))
{
return false;
}
return true;
}
private bool HasRoleAuth(string adAccount, String[] allowRoles)
{
//為了方便講解--------------------------------------------------------
var employees = new List<Employee>
{
new Employee { AD_ACCOUNT=@"MyCompany\John", Department="A部門", Name="John" },
new Employee { AD_ACCOUNT=@"MyCompany\Mary", Department="B部門", Name="Mary" },
new Employee { AD_ACCOUNT=@"MyCompany\Tom", Department="C部門", Name="Tom" },
};
//--------------------------------------------------------------------
var target = employees.Where(q => q.AD_ACCOUNT == adAccount).ToList();
if (target.Any() && allowRoles.Contains(target.First().Department))
{
return true;
}
else
{
return false;
}
}
}
}