ASP.NET Core 中 繼承的是AuthorizationHandler ,而ASP.NET Framework 中繼承的是AuthorizeAttribute.ajax
它們都是用太重寫裏面的方法實現過濾請求的。 數據庫
如今咱們實現如何在 ASP.NET Core MVC 實現自定義受權。框架
關於AuthorizationHandler 詳細介紹能夠看這裏異步
好比咱們後臺有個博客管理功能,那咱們能夠新建一個Blog的控制器,好比BlogControlleride
裏面有添加,刪除,編輯等功能,分別是Add,Delete,Edit函數
代碼以下測試
public class BlogController : Controller { public IActionResult Index() { return View(); } /// <summary> /// 博客添加頁面 /// </summary> /// <returns></returns> public IActionResult Add() { return View(); } /// <summary> /// 博客列表頁面 /// </summary> public IActionResult List() { return View(); } /// <summary> /// 博客編輯頁面 /// </summary> public IActionResult Edit() { return View(); } }
若是有打印能夠起個名字叫 public IActionResult Print()ui
自定義就是作個控制界面作勾選功能,用戶根據自身業務選擇。spa
以此類推,在ASP.NET 框架下默認路由就是Controller和Action,除非你修改默認路由,固然了你修改默認路由你的權限邏輯也得變。
AuthorizationHandler 參數裏面有個IAuthorizationRequirement要咱們去填充,根據咱們本身業務本身選擇定義數據。
public class PermissionRequirement : IAuthorizationRequirement { /// <summary> /// 無權限action /// </summary> public string DeniedAction { get; set; } = "/Home/visitDeny"; /// <summary> /// 認證受權類型 /// </summary> public string ClaimType { internal get; set; } /// <summary> /// 默認登陸頁面 /// </summary> public string LoginPath { get; set; } = "/Home/Login"; /// <summary> /// 過時時間 /// </summary> public TimeSpan Expiration { get; set; } /// <summary> /// 構造 /// </summary> /// <param name="deniedAction"></param> /// <param name="claimType"></param> /// <param name="expiration"></param> public PermissionRequirement(string deniedAction, string claimType, TimeSpan expiration) { ClaimType = claimType; DeniedAction = deniedAction; Expiration = expiration; } }
第一個參數集合
public class PermissionItem { /// <summary> /// 用戶或角色或其餘憑據名稱 /// </summary> public virtual string Role { get; set; } /// <summary> /// 配置的Controller名稱 /// </summary> public virtual string controllerName { get; set; } /// <summary> /// 配置的Action名稱 /// </summary> public virtual string actionName { get; set; } }
Startup 裏面,添加一個受權策略,PermissionRequirement 放進去,而後注入
////權限要求參數 var permissionRequirement = new PermissionRequirement( "/Home/visitDeny",// 拒絕受權的跳轉地址 ClaimTypes.Name,//基於用戶名的受權 expiration: TimeSpan.FromSeconds(60 * 5)//接口的過時時間 ); #endregion //【受權】 services.AddAuthorization(options => { options.AddPolicy("Permission", policy => policy.Requirements.Add(permissionRequirement)); }); // 注入權限處理器 services.AddTransient<IAuthorizationHandler, PermissionHandler>();
控制器裏面加上標示
[Authorize("Permission")] public class BlogController : Controller { }
[HttpPost] public async Task<IActionResult> Login(LoginViewModel model) { if (ModelState.IsValid) { if (model.textUser == null) { ModelState.AddModelError("", "請輸入帳號."); return View(model); } if (model.textPassword == null) { ModelState.AddModelError("", "請輸入密碼."); return View(model); } if (model.textUser == "admin" && model.textPassword == "123") { #region 傳統的登陸 //只判斷是否登陸 經過[Authorize] 小項目中只有一個管理員 只要帳號和密碼對就行 var claimIdentity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme); claimIdentity.AddClaim(new Claim(ClaimTypes.Name, model.textUser)); var claimsPrincipal = new ClaimsPrincipal(claimIdentity); //await HttpContext.SignInAsync(claimsPrincipal); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal); #endregion //下面代碼是演示的,實際項目要從根據用戶名或者角色從數據庫讀取出來 配置到 List<PermissionItem>裏面 //這裏我用的是用戶名判斷的,根據本身的業務本身處理 //測試的時候 能夠 刪除一條記錄試試,或者添加一條 List<PermissionItem> lsperm = new List<PermissionItem>(); lsperm.Add(new PermissionItem() { Role = model.textUser, controllerName = "Blog", actionName = "Add" });//添加博客頁面的權限 lsperm.Add(new PermissionItem() { Role = model.textUser, controllerName = "Blog", actionName = "Edit" });//編輯博客頁面的權限 lsperm.Add(new PermissionItem() { Role = model.textUser, controllerName = "Blog", actionName = "List" });//查看博客頁面的權限 string perData = JsonConvert.SerializeObject(lsperm); await _cacheService.SetStringAsync("perm" + model.textUser, perData); return RedirectToAction("Index", "Home"); } } return View(model); }
List<PermissionItem> 我用Redis存儲的,你們根據實際狀況存儲。
public class PermissionHandler : AuthorizationHandler<PermissionRequirement> { public IAuthenticationSchemeProvider Schemes; readonly IDistributedCache _cacheService; /// <summary> /// 構造函數注入 /// </summary> public PermissionHandler(IAuthenticationSchemeProvider schemes, IDistributedCache cacheService) { Schemes = schemes; _cacheService = cacheService; } // 重載異步處理程序 protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) { //從AuthorizationHandlerContext轉成HttpContext,以便取出表求信息 AuthorizationFilterContext filterContext = context.Resource as AuthorizationFilterContext; HttpContext httpContext = filterContext.HttpContext; AuthenticateResult result = await httpContext.AuthenticateAsync(Schemes.GetDefaultAuthenticateSchemeAsync().Result.Name); //若是沒登陸result.Succeeded爲false if (result.Succeeded) { httpContext.User = result.Principal; //當前訪問的Controller string controllerName = filterContext.RouteData.Values["Controller"].ToString();//經過ActionContext類的RouteData屬性獲取Controller的名稱:Home //當前訪問的Action string actionName = filterContext.RouteData.Values["Action"].ToString();//經過ActionContext類的RouteData屬性獲取Action的名稱:Index string name = httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Name)?.Value; string perData = await _cacheService.GetStringAsync("perm" + name); List<PermissionItem> lst = JsonConvert.DeserializeObject<List<PermissionItem>>(perData); if (lst.Where(w => w.controllerName == controllerName && w.actionName == actionName).Count() > 0) { //若是在配置的權限表裏正常走 context.Succeed(requirement); } else { //不在權限配置表裏 作錯誤提示 //若是是AJAX請求 (包含了VUE等 的ajax) string requestType = filterContext.HttpContext.Request.Headers["X-Requested-With"]; if (!string.IsNullOrEmpty(requestType) && requestType.Equals("XMLHttpRequest", StringComparison.CurrentCultureIgnoreCase)) { //ajax 的錯誤返回 //filterContext.Result = new StatusCodeResult(499); //自定義錯誤號 ajax請求錯誤 能夠用來錯沒有權限判斷 也能夠不寫 用默認的 context.Fail(); } else { //普通頁面錯誤提示 就是跳轉一個頁面 //httpContext.Response.Redirect("/Home/visitDeny");//第一種方式跳轉 filterContext.Result = new RedirectToActionResult("visitDeny", "Home", null);//第二種方式跳轉 context.Fail(); } } } else { context.Fail(); } } }
至此咱們實現定義受權判斷。實際業務上每一個人能夠根據本身的狀況作處理。