asp.net core策略授权
在《asp.net core认证与授权》中讲解了固定和自定义角色授权系统权限,其实我们还可以通过其他方式来授权,比如可以通过角色组,用户名,生日等,但这些主要取决于ClaimTypes,其实我们也可以自定义键值来授权,这些统一叫策略授权,其中更强大的是,我们可以自定义授权Handler来达到灵活授权,下面一一展开。
注意:下面的代码只是部分代码,完整代码参照:https://github.com/axzxs2001/Asp.NetCoreExperiment/tree/master/Asp.NetCoreExperiment/%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86/PolicyPrivilegeManagement
首先看基于角色组,或用户名,或基于ClaimType或自定义键值等授权策略,这些都是通过Services.AddAuthorization添加,并且是AuthorizationOptions来AddPolicy,这里策略的名称统一用RequireClaim来命名,不同的请求的策略名称各不相同,如用户名时就用policy.RequireUserName(),同时,在登录时,验证成功后,要添加相应的Claim到ClaimsIdentity中:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthorization(options =>
{
//基于角色组的策略
options.AddPolicy("RequireClaim", policy => policy.RequireRole("admin", "system"));
//基于用户名
//options.AddPolicy("RequireClaim", policy => policy.RequireUserName("桂素伟"));
//基于ClaimType
//options.AddPolicy("RequireClaim", policy => policy.RequireClaim(ClaimTypes.Country,"中国"));
//自定义值
// options.AddPolicy("RequireClaim", policy => policy.RequireClaim("date","2017-09-02"));
}).AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>{
options.LoginPath = new PathString("/login");
options.AccessDeniedPath = new PathString("/denied");
});
}
HomeController.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using PolicyPrivilegeManagement.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using System.Security.Claims; namespace PolicyPrivilegeManagement.Controllers
{
[Authorize(Policy = "RequireClaim")]
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
} public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
} public IActionResult Contact()
{
ViewData["Message"] = "Your contact page.";
return View();
} public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
[AllowAnonymous]
[HttpGet("login")]
public IActionResult Login(string returnUrl = null)
{
TempData["returnUrl"] = returnUrl;
return View();
}
[AllowAnonymous]
[HttpPost("login")]
public async Task<IActionResult> Login(string userName, string password, string returnUrl = null)
{
var list = new List<dynamic> {
new { UserName = "gsw", Password = "", Role = "admin",Name="桂素伟",Country="中国",Date="2017-09-02",BirthDay="1979-06-22"},
new { UserName = "aaa", Password = "", Role = "system",Name="测试A" ,Country="美国",Date="2017-09-03",BirthDay="1999-06-22"}
};
var user = list.SingleOrDefault(s => s.UserName == userName && s.Password == password);
if (user != null)
{
//用户标识
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.Sid, userName));
identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
identity.AddClaim(new Claim(ClaimTypes.Role, user.Role));
identity.AddClaim(new Claim(ClaimTypes.Country, user.Country));
identity.AddClaim(new Claim("date", user.Date)); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
if (returnUrl == null)
{
returnUrl = TempData["returnUrl"]?.ToString();
}
if (returnUrl != null)
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction(nameof(HomeController.Index), "Home");
}
}
else
{
const string badUserNameOrPasswordMessage = "用户名或密码错误!";
return BadRequest(badUserNameOrPasswordMessage);
}
}
[HttpGet("logout")]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return RedirectToAction("Index", "Home");
}
[AllowAnonymous]
[HttpGet("denied")]
public IActionResult Denied()
{
return View();
}
}
}
上面的授权策略都相对简单,单一,使用场景也很有限,就和固定角色授权如出一辙,其实可以用更好的来例用授权,那就是自定义授权Handler,我们在《asp.net core认证与授权》一文中,是通过中间件来达到自定义解色的,现在我们换个思路,通过自定义授权Handler来实现。
首先定义一个UserPermission,即用户权限实体类
/// <summary>
/// 用户权限
/// </summary>
public class UserPermission
{
/// <summary>
/// 用户名
/// </summary>
public string UserName
{ get; set; }
/// <summary>
/// 请求Url
/// </summary>
public string Url
{ get; set; }
}
接下来定义一个PermissionRequirement,为请求条件实体类
/// <summary>
/// 必要参数类
/// </summary>
public class PermissionRequirement : IAuthorizationRequirement
{
/// <summary>
/// 用户权限集合
/// </summary>
public List<UserPermission> UserPermissions { get;private set; }
/// <summary>
/// 无权限action
/// </summary>
public string DeniedAction { get; set; }
/// <summary>
/// 构造
/// </summary>
/// <param name="deniedAction">无权限action</param>
/// <param name="userPermissions">用户权限集合</param>
public PermissionRequirement(string deniedAction, List<UserPermission> userPermissions)
{
DeniedAction = deniedAction;
UserPermissions = userPermissions;
}
}
再定义自定义授权Hanlder,我们命名为PermissionHandler,此类必需继承AuthorizationHandler<T>,只用实现public virtual Task HandleAsync(AuthorizationHandlerContext context),些方法是用户请求时验证是否授权的主方法,所以实现与自定义角色中间件的Invoke很相似。
using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks; namespace PolicyPrivilegeManagement.Models
{
/// <summary>
/// 权限授权Handler
/// </summary>
public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{
/// <summary>
/// 用户权限
/// </summary>
public List<UserPermission> UserPermissions { get; set; } protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
//赋值用户权限
UserPermissions = requirement.UserPermissions;
//从AuthorizationHandlerContext转成HttpContext,以便取出表求信息
var httpContext = (context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext).HttpContext;
//请求Url
var questUrl = httpContext.Request.Path.Value.ToLower();
//是否经过验证
var isAuthenticated = httpContext.User.Identity.IsAuthenticated;
if (isAuthenticated)
{
if (UserPermissions.GroupBy(g => g.Url).Where(w => w.Key.ToLower() == questUrl).Count() > )
{
//用户名
var userName = httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Sid).Value;
if (UserPermissions.Where(w => w.UserName == userName && w.Url.ToLower() == questUrl).Count() > )
{
context.Succeed(requirement);
}
else
{
//无权限跳转到拒绝页面
httpContext.Response.Redirect(requirement.DeniedAction );
}
}
else
{
context.Succeed(requirement);
}
}
return Task.CompletedTask;
}
}
}
此次的Startup.cs的ConfigureServices发生了变化,如下
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthorization(options =>
{
//自定义Requirement,userPermission可从数据库中获得
var userPermission= new List<UserPermission> {
new UserPermission { Url="/", UserName="gsw"},
new UserPermission { Url="/home/permissionadd", UserName="gsw"},
new UserPermission { Url="/", UserName="aaa"},
new UserPermission { Url="/home/contact", UserName="aaa"}
}; options.AddPolicy("Permission",
policy => policy.Requirements.Add(new PermissionRequirement("/denied", userPermission))); }).AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>{
options.LoginPath = new PathString("/login");
options.AccessDeniedPath = new PathString("/denied"); });
//注入授权Handler
services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
}
HomeController中代码如下:
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using PolicyPrivilegeManagement.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using System.Security.Claims; namespace PolicyPrivilegeManagement.Controllers
{
[Authorize(Policy = "Permission")]
public class HomeController : Controller
{
PermissionHandler _permissionHandler;
public HomeController(IAuthorizationHandler permissionHandler)
{
_permissionHandler = permissionHandler as PermissionHandler;
}
public IActionResult Index()
{
return View();
} public IActionResult PermissionAdd()
{
return View();
} [HttpPost("addpermission")]
public IActionResult AddPermission(string url,string userName)
{
//添加权限
_permissionHandler.UserPermissions.Add(new UserPermission { Url = url, UserName = userName });
return Content("添加成功");
} public IActionResult Contact()
{
ViewData["Message"] = "Your contact page."; return View();
} public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
[AllowAnonymous]
[HttpGet("login")]
public IActionResult Login(string returnUrl = null)
{
TempData["returnUrl"] = returnUrl;
return View();
}
[AllowAnonymous]
[HttpPost("login")]
public async Task<IActionResult> Login(string userName, string password, string returnUrl = null)
{
var list = new List<dynamic> {
new { UserName = "gsw", Password = "", Role = "admin",Name="桂素伟",Country="中国",Date="2017-09-02",BirthDay="1979-06-22"},
new { UserName = "aaa", Password = "", Role = "system",Name="测试A" ,Country="美国",Date="2017-09-03",BirthDay="1999-06-22"}
};
var user = list.SingleOrDefault(s => s.UserName == userName && s.Password == password);
if (user != null)
{
//用户标识
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.Sid, userName));
identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
identity.AddClaim(new Claim(ClaimTypes.Role, user.Role));
identity.AddClaim(new Claim(ClaimTypes.Country, user.Country));
identity.AddClaim(new Claim("date", user.Date)); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
if (returnUrl == null)
{
returnUrl = TempData["returnUrl"]?.ToString();
}
if (returnUrl != null)
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction(nameof(HomeController.Index), "Home");
}
}
else
{
const string badUserNameOrPasswordMessage = "用户名或密码错误!";
return BadRequest(badUserNameOrPasswordMessage);
}
}
[HttpGet("logout")]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return RedirectToAction("Index", "Home");
}
[AllowAnonymous]
[HttpGet("denied")]
public IActionResult Denied()
{
return View();
}
}
}
本例设计是当用户gsw密码111111登录时,是不能访问/home/contact的,刚登录时访该action是不成功的,这里我们在/home/addpermission中添加一个Action名称:/home/contact,用户名:gsw的信息,此时再访问/home/contact,会发现是可以访问的,这是因为我们热更新了PermissionHandler中的用户权限集合,用户的权限得到了扩展和变化。
其实用中间件能达到灵活权限的设置,用自定义授权Handler也可以,接下来比较一下两种做法的优劣:
中间件 |
自定义授权Handler |
|
用户权限集合 |
静态对象 |
实体化对象 |
热更新时 |
用中间件名称.用户权限集合更新 |
因为在Startup.cs中,PermissionHandler是依赖注放的,可以在热更新的构造中获取并操作 |
性能方面 |
每个action请求都会触发Invock方法,标记[AllowAnonymous]特性的Action也会触发 |
只有标记[Authorize]特性的Action会触发该方法,标记[AllowAnonymous]特性的Action不会触发,性能更优化 |
最后,把授权策略做了个NuGet的包,大家可在asp.net core 2.0的项目中查询 AuthorizePolicy引用使用这个包,包对应的github地址:https://github.com/axzxs2001/AuthorizePolicy,欢迎大家提出建议,来共同完善这个授权策略。
asp.net core策略授权的更多相关文章
- ASP.NET Core策略授权和 ABP 授权
目录 ASP.NET Core 中的策略授权 策略 定义一个 Controller 设定权限 定义策略 存储用户信息 标记访问权限 认证:Token 凭据 颁发登录凭据 自定义授权 IAuthoriz ...
- ASP.NET Core MVC 授权的扩展:自定义 Authorize Attribute 和 IApplicationModelProvide
一.概述 ASP.NET Core MVC 提供了基于角色( Role ).声明( Chaim ) 和策略 ( Policy ) 等的授权方式.在实际应用中,可能采用部门( Department , ...
- asp.net core的授权过滤器中获取action上的Attribute
var action = context.ActionDescriptor as ControllerActionDescriptor; var permission = action.MethodI ...
- ASP.NET Core WebAPI中使用JWT Bearer认证和授权
目录 为什么是 JWT Bearer 什么是 JWT JWT 的优缺点 在 WebAPI 中使用 JWT 认证 刷新 Token 使用授权 简单授权 基于固定角色的授权 基于策略的授权 自定义策略授权 ...
- ASP.NET Core 认证与授权[6]:授权策略是怎么执行的?
在上一章中,详细介绍了 ASP.NET Core 中的授权策略,在需要授权时,只需要在对应的Controler或者Action上面打上[Authorize]特性,并指定要执行的策略名称即可,但是,授权 ...
- ASP.NET Core 认证与授权[5]:初识授权
经过前面几章的姗姗学步,我们了解了在 ASP.NET Core 中是如何认证的,终于来到了授权阶段.在认证阶段我们通过用户令牌获取到用户的Claims,而授权便是对这些的Claims的验证,如:是否拥 ...
- ASP.NET Core 认证与授权[7]:动态授权
ASP.NET Core 中基于策略的授权旨在分离授权与应用程序逻辑,它提供了灵活的策略定义模型,在一些权限固定的系统中,使用起来非常方便.但是,当要授权的资源无法预先确定,或需要将权限控制到每一个具 ...
- ASP.NET Core 2.2 : 二十七. JWT与用户授权(细化到Action)
上一章分享了如何在ASP.NET Core中应用JWT进行用户认证以及Token的刷新,本章继续进行下一步,用户授权.涉及到的例子也以上一章的为基础.(ASP.NET Core 系列目录) 一.概述 ...
- asp.net core 3.x 授权默认流程
一.前言 接上一篇<asp.net core 3.x 授权中的概念>,本篇看看asp.net core默认授权的流程.从两个方面来看整个授权系统是怎么运行的:启动阶段的配置.请求阶段中间件 ...
随机推荐
- vue怎么样创建组件呢??
我知道vue中核心就是组件,但是组件是什么呢?组件有什么用呢?怎么用组件呢?怎么样创建自己的组件呢? 前面两个问题就不说了,这里来说说,后面的两个问题: 1)创建自己的组件 通过vue.extend( ...
- python爬虫从入门到放弃(一)之初识爬虫
整理这个文档的初衷是自己开始学习的时候没有找到好的教程和文本资料,自己整理一份这样的资料希望能对小伙伴有帮助 什么是爬虫? 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页 ...
- opnet的simple_source模块学习 分类: opnet 2014-05-18 09:50 170人阅读 评论(0) 收藏
simple_source模块可以在外部设置的属性 有四个局部统计量,分别为产生的bit速率.包速率.包大小,包间隔 状态机为三个非强制对象,在头文件里定义了自中断和转移条件. /*Include f ...
- 一次花费了一两个小时的mysql问题排查
晚上把博客迁了个服务器,新建用户的时候遇到问题了. 关于mysql的问题. 前置操作 建了两个用户,一个laravel,一个blog用户以及他们的同名数据库. 建好之后,命令行下面连接mysql服务, ...
- [转]为什么大型网站前端使用 PHP 后台逻辑用 Java?
最近纠结了一下,如果开发一个大型的网站,我到底应该使用php还是jsp,后台到底使用php还是用java,我的选择要么是php要么是java,因为我喜欢linux.unix,当然window平台也必须 ...
- Angular2 VS Angular4 深度对比:特性、性能
欢迎大家持续关注葡萄城控件技术团队博客,更多更好的原创文章尽在这里~~ 在Web应用开发领域,Angular被认为是最好的开源JavaScript框架之一. Google的Angular团队已于3月 ...
- Open-Falcon第五步安装Query(小米开源互联网企业级监控系统)
安装Query query组件,绘图数据的查询接口,query组件收到用户的查询请求后,会从后端的多个graph,查询相应的数据,聚合后,再返回给用户. cd /usr/local/open-falc ...
- 安装nginx+ngx_lua支持WAF防护功能
安装nginx+ngx_lua支持WAF防护功能 nginx lua模块淘宝开发的nginx第三方模块,它能将lua语言嵌入到nginx配置中,从而使用lua就极大增强了nginx的能力.nginx以 ...
- Git分支使用心得
在去年的大约这个时候,我的领导让我研究一下git的使用方法,方便我们自己的代码管理,因为我们原先使用的是SVN,使用起来没那么方便,所以让我研究研究git的使用.我就简单的研究了两天,用我的IDE(v ...
- 高性能MySQL之【第十五章 备份与恢复】学习记录
我们不打算包括的话题: 安全(访问备份,恢复数据的权限,文件是否需要加密) 备份存储在哪里,包括他们应该离源数据多远,以及如何将数据从源头移动到目的地 保留策略.审计 ...