asp.net Core 中AuthorizationHandler 实现自定义授权
前言
ASP.NET Core 中 继承的是AuthorizationHandler ,而ASP.NET Framework 中继承的是AuthorizeAttribute.
它们都是用过重写里面的方法实现过滤请求的。
现在我们实现如何在 ASP.NET Core MVC 实现自定义授权。
关于AuthorizationHandler 详细介绍可以看这里
如何自定义授权
比如我们后台有个博客管理功能,那我们可以新建一个Blog的控制器,比如BlogController
里面有添加,删除,编辑等功能,分别是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()
自定义就是做个控制界面做勾选功能,用户根据自身业务选择。

以此类推,在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( * )//接口的过期时间
);
#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 == "")
{
#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() > )
{
//如果在配置的权限表里正常走
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();
}
}
}
至此我们实现定义授权判断。实际业务上每个人可以根据自己的情况做处理。
asp.net Core 中AuthorizationHandler 实现自定义授权的更多相关文章
- 第十五节:Asp.Net Core中的各种过滤器(授权、资源、操作、结果、异常)
一. 简介 1. 说明 提到过滤器,通常是指请求处理管道中特定阶段之前或之后的代码,可以处理:授权.响应缓存(对请求管道进行短路,以便返回缓存的响应). 防盗链.本地化国际化等,过滤器用于横向处理业务 ...
- asp.net core中遇到需要自定义数据包解密方法的时候
最近将公司的项目用.netcore重写, 服务的http外部接口部分收发消息是DES加解密的, 那么在asp.net core mvc的action处理之前需要加入解密这个步骤. 我第一想到的是用fi ...
- 从零搭建一个IdentityServer——聊聊Asp.net core中的身份验证与授权
OpenIDConnect是一个身份验证服务,而Oauth2.0是一个授权框架,在前面几篇文章里通过IdentityServer4实现了基于Oauth2.0的客户端证书(Client_Credenti ...
- Asp.Net Core 中IdentityServer4 授权中心之自定义授权模式
一.前言 上一篇我分享了一篇关于 Asp.Net Core 中IdentityServer4 授权中心之应用实战 的文章,其中有不少博友给我提了问题,其中有一个博友问我的一个场景,我给他解答的还不够完 ...
- Asp.Net Core 中IdentityServer4 授权原理及刷新Token的应用
一.前言 上面分享了IdentityServer4 两篇系列文章,核心主题主要是密码授权模式及自定义授权模式,但是仅仅是分享了这两种模式的使用,这篇文章进一步来分享IdentityServer4的授权 ...
- Asp.Net Core 中IdentityServer4 实战之角色授权详解
一.前言 前几篇文章分享了IdentityServer4密码模式的基本授权及自定义授权等方式,最近由于改造一个网关服务,用到了IdentityServer4的授权,改造过程中发现比较适合基于Role角 ...
- ASP.NET Core中显示自定义错误页面-增强版
之前的博文 ASP.NET Core中显示自定义错误页面 中的方法是在项目中硬编码实现的,当有多个项目时,就会造成不同项目之间的重复代码,不可取. 在这篇博文中改用middleware实现,并且放在独 ...
- ASP.NET Core中显示自定义错误页面
在 ASP.NET Core 中,默认情况下当发生500或404错误时,只返回http状态码,不返回任何内容,页面一片空白. 如果在 Startup.cs 的 Configure() 中加上 app. ...
- 如何在ASP.NET Core中自定义Azure Storage File Provider
文章标题:如何在ASP.NET Core中自定义Azure Storage File Provider 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p ...
随机推荐
- 0513JS数组的定义、遍历、添加
|数组|-定义方式|--1.new Array();|----空数组|------var attr = new Array();|------lenght:0|------_proto_: Array ...
- c语言中realloc()函数解析
一.基本特性 1. realloc()函数可以重用或扩展以前用malloc().calloc()及realloc()函数自身分配的内存. 2. realloc()函数需两个参数:一个是包含地址的指针( ...
- Cookie、Session、jsp、EL、JSTL
会话技术 Cookie Session 从访问一个站点,到关闭不继续访问 称为一次会话过程.会话技术就是记录本次会话中客户端的状态与数据的. 会话技术分为cookie.session. cooki ...
- vue技术分享-你可能不知道的7个秘密
前言 本文是vue源码贡献值Chris Fritz在公共场合的一场分享,觉得分享里面有不少东西值得借鉴,虽然有些内容我在工作中也是这么做的,还是把大神的ppt在这里翻译一下,希望给朋友带来一些帮助. ...
- CentOS6.7 mysql5.6.33修改数据文件位置
问题:mysql存放的数据文件,分区容量较小,目前已经满,导致mysql连接不上, 解决方案: 1.删除分区里一个不需要用的数据,如:日志文件等(解决不了根本问题) 2.对某个磁盘扩容 3.修改数据存 ...
- Linux时间子系统之五:低分辨率定时器的原理和实现
专题文档汇总目录 Notes:低精度timer在内核中的数据结构以及API接口:低精度timer精巧高效的分组,使用cascade进行定时器移位,组内Timer FIFO:低精度Timer的初始化流程 ...
- Web开发:需收藏的JavaScript常用技巧
原文来自:https://www.baidu.com/home/news/data/newspage?nid=4242707244478773456&n_type=0&p_from=1 ...
- python基础下的mysql学习之操作命令(一)
E-R模型简介: 该模型应用于数据库设计中的概念结构设计阶段 E-entry,实体,是指设计的主体.对象等: R-relationship,关系,是指设计的主体之间的联系,关系也是一种数据: 一个实体 ...
- Java Script 读书笔记 (三) 函数
1. 函数作用域 在函数内部定义的变量,外部无法读取,称为"局部变量"(local variable). 变量v在函数内部定义,所以是一个局部变量,函数之外就无法读取. 函数内部定 ...
- redis结合自定义注解实现基于方法的注解缓存,及托底缓存的实现
本次分享如何使用redis结合自定义注解实现基于方法的注解缓存,及托底缓存的实现思路 现在的互联网公司大多数都是以Redis作为缓存,使用缓存的优点就不赘述了,写这篇文章的目的就是想帮助同学们如 ...