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默认授权的流程.从两个方面来看整个授权系统是怎么运行的:启动阶段的配置.请求阶段中间件 ...
随机推荐
- PhpStrom如何安装主题?(总结三种不同格式安装方法)
在网上搜了很多都是不怎么齐全的方法,在这里我总结一下PhpStrom的三种不同格式的安装方法,以后就不用再去网上搜直接看我自己的博客就知道了.以下默认为Windows系统,Linux现在暂时还没更新. ...
- 分享一个PHP文件上传类
该类用于处理文件上传,可以上传一个文件,也可以上传多个文件. 包括的成员属性有: private $path = "./uploads"; //上传文件保存的路径 private ...
- LoadRunner性能测试-LoadRunner工具安装
Loadrunner11安装 1,在网上下载loadrunner11安装包 (百度可以下载安装包) 2,打开安装文件,点击setup.exe进行安装: 3,win10系统可能会出现如下问题: 提示管理 ...
- Failed to load JavaHL Library. These are the errors that were encountered:
问题: Failed to load JavaHL Library.These are the errors that were encountered:no msvcp100 in java.lib ...
- iOS9中关于 NSURLSession/NSURLConnection HTTP load failed 的解决办法
最近为了新的存管app上线,忙了近一个月,重新过了一段996的日子,今天终于可以喘口气,继续更新博客了.本文记录一下在iOS 9中发送https请求遇到的问题及解决办法,希望通过本文,可以对ATS的配 ...
- 深入分析动态管理Fragment
一.Fragment与Activity的关系 要理解动态管理Fragment首先要理解一下Fragment与Activity的关系 Fragment可以理解为将Activity划分为若干个片段,Fra ...
- markdownpad生成目录
生成目录 document.addEventListener("DOMContentLoaded", function() { // 生成目录列表 var outline = do ...
- C#高级编程:泛型优点和特性
泛型是CLR 2.0的一个新特性,在CLR 1.0中,要创建一个灵活的类或方法,但该类或方法在编译期间不知道使用什么类,就得以Object类为基础.而Object在编译期间没有类型安全性,因此必须进行 ...
- OpenCms模块创建图解
登录OpenCms后,切换到"管理(Administration)"视图,点击"模块管理",这时窗口显示已安装模块的列表. 确定当前不在"online ...
- Windows10系统下,彻底删除卸载MySQL
本文介绍,在Windows10系统下,如何彻底删除卸载MySQL... 1>停止MySQL服务开始->所有应用->Windows管理工具->服务,将MySQL服务停止.2> ...