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(); } } }
至此咱们实现定义受权判断。实际业务上每一个人能够根据本身的状况作处理。