前言

ASP.NET Core 中 继承的是AuthorizationHandler ,而ASP.NET Framework 中继承的是AuthorizeAttribute.

它们都是用过重写里面的方法实现过滤请求的。

现在我们实现如何在 ASP.NET Core MVC 实现自定义授权。

关于AuthorizationHandler 详细介绍可以看这里

https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.2#authorization-handlers

如何自定义授权

比如我们后台有个博客管理功能,那我们可以新建一个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 实现自定义授权的更多相关文章

  1. 第十五节:Asp.Net Core中的各种过滤器(授权、资源、操作、结果、异常)

    一. 简介 1. 说明 提到过滤器,通常是指请求处理管道中特定阶段之前或之后的代码,可以处理:授权.响应缓存(对请求管道进行短路,以便返回缓存的响应). 防盗链.本地化国际化等,过滤器用于横向处理业务 ...

  2. asp.net core中遇到需要自定义数据包解密方法的时候

    最近将公司的项目用.netcore重写, 服务的http外部接口部分收发消息是DES加解密的, 那么在asp.net core mvc的action处理之前需要加入解密这个步骤. 我第一想到的是用fi ...

  3. 从零搭建一个IdentityServer——聊聊Asp.net core中的身份验证与授权

    OpenIDConnect是一个身份验证服务,而Oauth2.0是一个授权框架,在前面几篇文章里通过IdentityServer4实现了基于Oauth2.0的客户端证书(Client_Credenti ...

  4. Asp.Net Core 中IdentityServer4 授权中心之自定义授权模式

    一.前言 上一篇我分享了一篇关于 Asp.Net Core 中IdentityServer4 授权中心之应用实战 的文章,其中有不少博友给我提了问题,其中有一个博友问我的一个场景,我给他解答的还不够完 ...

  5. Asp.Net Core 中IdentityServer4 授权原理及刷新Token的应用

    一.前言 上面分享了IdentityServer4 两篇系列文章,核心主题主要是密码授权模式及自定义授权模式,但是仅仅是分享了这两种模式的使用,这篇文章进一步来分享IdentityServer4的授权 ...

  6. Asp.Net Core 中IdentityServer4 实战之角色授权详解

    一.前言 前几篇文章分享了IdentityServer4密码模式的基本授权及自定义授权等方式,最近由于改造一个网关服务,用到了IdentityServer4的授权,改造过程中发现比较适合基于Role角 ...

  7. ASP.NET Core中显示自定义错误页面-增强版

    之前的博文 ASP.NET Core中显示自定义错误页面 中的方法是在项目中硬编码实现的,当有多个项目时,就会造成不同项目之间的重复代码,不可取. 在这篇博文中改用middleware实现,并且放在独 ...

  8. ASP.NET Core中显示自定义错误页面

    在 ASP.NET Core 中,默认情况下当发生500或404错误时,只返回http状态码,不返回任何内容,页面一片空白. 如果在 Startup.cs 的 Configure() 中加上 app. ...

  9. 如何在ASP.NET Core中自定义Azure Storage File Provider

    文章标题:如何在ASP.NET Core中自定义Azure Storage File Provider 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p ...

随机推荐

  1. jsp 增删改查

    使用Idea创建项目 1.新建web application项目 Idea 选择 Java Enterprise -> web application 2.新版本没有web-inf文件夹 解决方 ...

  2. .NET开发设计模式-模板模式

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  3. arcEngine开发之加载栅格数据

    加载数据思路 在Engine中加载各种各样的数据都是通过这样的格式 IWorkspaceFactory pWorkspace = new IWorkspaceFactory(); IWorkspace ...

  4. iOS 中判断应用程序是否为第一次打开

    第一步:在AppDelegate中当应用启动完成后加入一下代码: - (BOOL)application:(UIApplication *)application didFinishLaunching ...

  5. 第五章——支持向量机(Support Vector Machines)

    svm可用于线性或非线性分类.回归.甚至异常检测. svm尤其适用于中小数据集的复杂分类问题. 5.1 Linear SVM Classification svm对feature scales敏感,如 ...

  6. Scrapy 1.4 文档 05 命令行工具

    在系统命令行中,使用 scrapy 命令可以创建工程或启动爬虫,它控制着 Scrapy 的行为,我们称之为 Scrapy 命令行工具(command-line tool)或 Scrapy 工具(Scr ...

  7. JavaScript设计模式 Item 7 --策略模式Strategy

    1.策略模式的定义 何为策略?比如我们要去某个地方旅游,可以根据具体的实际情况来选择出行的线路. 如果没有时间但是不在乎钱,可以选择坐飞机. 如果没有钱,可以选择坐大巴或者火车. 如果再穷一点,可以选 ...

  8. C语言gets雨scanf函数的用法

    /*1.不同点: scanf不能接受空格.制表符Tab.回车等: 而gets能够接受空格.制表符Tab和回车等: 2.相同点:  字符串接受结束后自动加'\0'. 使用scanf("%s&q ...

  9. nsq topic和channel的区别

    topic:一个可供订阅的话题.channel:属于topic的下一级,一个topic可以有多个channel.二者关系可以再参考下面两文章:http://www.cnblogs.com/forres ...

  10. Loadrunner下载脚本

    由于最近又在SGM做性能测试,扒拉出一篇去年5.6月份的一个脚本. 最近写的翻来看看其实也蛮简单的,还是就不放博客了. Action(){ //定义文件大小 int flen; //定义响应数据内容大 ...