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 ...
随机推荐
- Caused by: android.view.InflateException: Binary XML file line #2: Error inflating class android.sup
解决:找不到资源文件: 系统会根据分辨率来选择加载不同drawable下文件夹的资源,如果只在一个文件下放了资源文件,不同的分辨率设备的会报错.
- git基础命令学习总结
git版本升级 git clone git://git.kernel.org/pub/scm/git/git.git 列出所有 Git 当时能找到的配置 git config --list git c ...
- Http协议、Tomcat、servlet
HTTP协议 Http,超文本传输协议是互联网上最广泛的一种网络协议,所有的www文件都必须遵守这个标准. Http协议由http请求和http响应组成 http请求: 1.请求行 请求方式 POST ...
- 50行ruby代码开发一个区块链
区块链是什么?作为一个Ruby开发者,理解区块链的最好办法,就是亲自动手实现一个.只需要50行Ruby代码你就能彻底理解区块链的核心原理! 区块链 = 区块组成的链表? blockchain.ruby ...
- Struts2文件上传--多文件上传(插件uploadify)
公司需要把以前的Struts2自带的图片上传替换掉,因为不能一个file选择多个文件,本人直接百度搜索图片插件, 貌似就它(uploadify3.2.1)在最前面,也找过很多案例, 其中有不少问题, ...
- Linux文本处理命令 -- grep
简介 grep (global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它 ...
- JS中清空字符串前后空格
在JS 操作的时候 长期会有对字符串的操作, 但是在JS 中不像在C#中 有字符串的方法 Trim() 来对字符串进行处理 . 所以自己写一个对字符串前后清楚空格的方法还是有必要的 以免以后忘记了 ...
- centos7搭建postfix邮件服务器
在使用qq等邮件服务器厂商提供的邮件服务后,发现他们的邮件发送数量是有限制的,随着公司的业务的需求下,我们需要搭建一个邮件服务器,邮件服务器可以帮助我们在一些提醒方面和消息推送方面起到帮助. 理论性语 ...
- Android 自定义 HorizontalScrollView 打造再多图片(控件)也不怕 OOM 的横向滑动效果
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38140505 自从Gallery被谷歌废弃以后,Google推荐使用ViewPa ...
- requests+selenium==requestium模块介绍
有时,你可能会在网上实现一些自动化操作.比如抓取网站,进行应用测试,或在网上填表,但又不想使用API,这时自动化就变得很必要.Python提供了非常优秀的Requests库可以辅助进行这些操作.可惜, ...