MyException - 我的异常网
当前位置:我的异常网» ASP » asp.net core谋略授权

asp.net core谋略授权

www.MyException.Cn  网友分享于:2013-09-19  浏览:0次
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

 1         public void ConfigureServices(IServiceCollection services)
 2         {
 3             services.AddMvc();
 4             services.AddAuthorization(options =>
 5             {
 6 //基于角色组的策略
 7                 options.AddPolicy("RequireClaim", policy => policy.RequireRole("admin", "system"));
 8                 //基于用户名
 9                 //options.AddPolicy("RequireClaim", policy => policy.RequireUserName("桂素伟"));
10                 //基于ClaimType
11                 //options.AddPolicy("RequireClaim", policy => policy.RequireClaim(ClaimTypes.Country,"中国"));
12                 //自定义值
13                 // options.AddPolicy("RequireClaim", policy => policy.RequireClaim("date","2017-09-02"));                
14             }).AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>{
15                 options.LoginPath = new PathString("/login");
16                 options.AccessDeniedPath = new PathString("/denied");
17             }); 
18         }

HomeController.cs

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Diagnostics;
 4 using System.Linq;
 5 using System.Threading.Tasks;
 6 using Microsoft.AspNetCore.Mvc;
 7 using PolicyPrivilegeManagement.Models;
 8 using Microsoft.AspNetCore.Authorization;
 9 using Microsoft.AspNetCore.Authentication;
10 using Microsoft.AspNetCore.Authentication.Cookies;
11 using System.Security.Claims;
12 
13 namespace PolicyPrivilegeManagement.Controllers
14 {
15     [Authorize(Policy = "RequireClaim")]
16     public class HomeController : Controller
17     {       
18         public IActionResult Index()
19         {
20             return View();
21         }
22 
23         public IActionResult About()
24         {
25             ViewData["Message"] = "Your application description page.";
26             return View();
27         }
28         
29         public IActionResult Contact()
30         {
31             ViewData["Message"] = "Your contact page.";
32             return View();
33         }
34 
35         public IActionResult Error()
36         {
37             return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
38         }
39         [AllowAnonymous]
40         [HttpGet("login")]
41         public IActionResult Login(string returnUrl = null)
42         {
43             TempData["returnUrl"] = returnUrl;
44             return View();
45         }
46         [AllowAnonymous]
47         [HttpPost("login")]
48         public async Task<IActionResult> Login(string userName, string password, string returnUrl = null)
49         {
50             var list = new List<dynamic> {
51                 new { UserName = "gsw", Password = "111111", Role = "admin",Name="桂素伟",Country="中国",Date="2017-09-02",BirthDay="1979-06-22"},
52                 new { UserName = "aaa", Password = "222222", Role = "system",Name="测试A" ,Country="美国",Date="2017-09-03",BirthDay="1999-06-22"}
53             };
54             var user = list.SingleOrDefault(s => s.UserName == userName && s.Password == password);
55             if (user != null)
56             {
57                 //用户标识
58                 var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
59                 identity.AddClaim(new Claim(ClaimTypes.Sid, userName));
60                 identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
61                 identity.AddClaim(new Claim(ClaimTypes.Role, user.Role));
62                 identity.AddClaim(new Claim(ClaimTypes.Country, user.Country));
63                 identity.AddClaim(new Claim("date", user.Date));
64 
65                 await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
66                 if (returnUrl == null)
67                 {
68                     returnUrl = TempData["returnUrl"]?.ToString();
69                 }
70                 if (returnUrl != null)
71                 {
72                     return Redirect(returnUrl);
73                 }
74                 else
75                 {
76                     return RedirectToAction(nameof(HomeController.Index), "Home");
77                 }
78             }
79             else
80             {
81                 const string badUserNameOrPasswordMessage = "用户名或密码错误!";
82                 return BadRequest(badUserNameOrPasswordMessage);
83             }
84         }
85         [HttpGet("logout")]
86         public async Task<IActionResult> Logout()
87         {
88             await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
89             return RedirectToAction("Index", "Home");
90         }
91         [AllowAnonymous]
92         [HttpGet("denied")]
93         public IActionResult Denied()
94         {
95             return View();
96         }
97     }
98 }

上面的授权策略都相对简单,单一,使用场景也很有限,就和固定角色授权如出一辙,其实可以用更好的来例用授权,那就是自定义授权Handler,我们在《asp.net core认证与授权》一文中,是通过中间件来达到自定义解色的,现在我们换个思路,通过自定义授权Handler来实现。

首先定义一个UserPermission,即用户权限实体类

 1 /// <summary>
 2     /// 用户权限
 3     /// </summary>
 4     public class UserPermission
 5     {
 6         /// <summary>
 7         /// 用户名
 8         /// </summary>
 9         public string UserName
10         { get; set; }
11         /// <summary>
12         /// 请求Url
13         /// </summary>
14         public string Url
15         { get; set; }
16     }

接下来定义一个PermissionRequirement,为请求条件实体类

 1 /// <summary>
 2     /// 必要参数类
 3     /// </summary>
 4     public class PermissionRequirement : IAuthorizationRequirement
 5     {
 6         /// <summary>
 7         /// 用户权限集合
 8         /// </summary>
 9         public  List<UserPermission> UserPermissions { get;private set; }
10         /// <summary>
11         /// 无权限action
12         /// </summary>
13         public string DeniedAction { get; set; }
14         /// <summary>
15         /// 构造
16         /// </summary>
17         /// <param name="deniedAction">无权限action</param>
18         /// <param name="userPermissions">用户权限集合</param>
19         public PermissionRequirement(string deniedAction, List<UserPermission> userPermissions)
20         {
21             DeniedAction = deniedAction;
22             UserPermissions = userPermissions;
23         }
24     }

再定义自定义授权Hanlder,我们命名为PermissionHandler,此类必需继承AuthorizationHandler<T>,只用实现public virtual Task HandleAsync(AuthorizationHandlerContext context),些方法是用户请求时验证是否授权的主方法,所以实现与自定义角色中间件的Invoke很相似。

 1 using Microsoft.AspNetCore.Authorization;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Security.Claims;
 5 using System.Threading.Tasks;
 6 
 7 namespace PolicyPrivilegeManagement.Models
 8 {
 9     /// <summary>
10     /// 权限授权Handler
11     /// </summary>
12     public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
13     {
14         /// <summary>
15         /// 用户权限
16         /// </summary>
17         public List<UserPermission> UserPermissions { get; set; }
18 
19         protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
20         {
21             //赋值用户权限
22             UserPermissions = requirement.UserPermissions;
23             //从AuthorizationHandlerContext转成HttpContext,以便取出表求信息
24             var httpContext = (context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext).HttpContext;
25             //请求Url
26             var questUrl = httpContext.Request.Path.Value.ToLower();
27             //是否经过验证
28             var isAuthenticated = httpContext.User.Identity.IsAuthenticated;
29             if (isAuthenticated)
30             {
31                 if (UserPermissions.GroupBy(g => g.Url).Where(w => w.Key.ToLower() == questUrl).Count() > 0)
32                 {
33                     //用户名
34                     var userName = httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Sid).Value;
35                     if (UserPermissions.Where(w => w.UserName == userName && w.Url.ToLower() == questUrl).Count() > 0)
36                     {
37                         context.Succeed(requirement);
38                     }
39                     else
40                     {
41                         //无权限跳转到拒绝页面
42                         httpContext.Response.Redirect(requirement.DeniedAction );
43 }
44 }
45 else
46 {
47 context.Succeed(requirement);
48 }
49 }
50 return Task.CompletedTask;
51 }
52 }
53 }

此次的Startup.cs的ConfigureServices发生了变化,如下

 1      public void ConfigureServices(IServiceCollection services)
 2         {
 3             services.AddMvc();
 4             services.AddAuthorization(options =>
 5             {  
 6                  //自定义Requirement,userPermission可从数据库中获得
 7                 var userPermission= new List<UserPermission> {
 8                               new UserPermission {  Url="/", UserName="gsw"},
 9                               new UserPermission {  Url="/home/permissionadd", UserName="gsw"},
10                               new UserPermission {  Url="/", UserName="aaa"},
11                               new UserPermission {  Url="/home/contact", UserName="aaa"}
12                           };
13 
14                 options.AddPolicy("Permission",
15                           policy => policy.Requirements.Add(new PermissionRequirement("/denied", userPermission)));
16 
17             }).AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>{
18                 options.LoginPath = new PathString("/login");
19                 options.AccessDeniedPath = new PathString("/denied");
20 
21             });
22             //注入授权Handler
23             services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
24         }

HomeController中代码如下:

  1 using System.Collections.Generic;
  2 using System.Diagnostics;
  3 using System.Linq;
  4 using System.Threading.Tasks;
  5 using Microsoft.AspNetCore.Mvc;
  6 using PolicyPrivilegeManagement.Models;
  7 using Microsoft.AspNetCore.Authorization;
  8 using Microsoft.AspNetCore.Authentication;
  9 using Microsoft.AspNetCore.Authentication.Cookies;
 10 using System.Security.Claims;
 11 
 12 namespace PolicyPrivilegeManagement.Controllers
 13 {
 14     [Authorize(Policy = "Permission")]   
 15     public class HomeController : Controller
 16     {
 17         PermissionHandler _permissionHandler;
 18         public HomeController(IAuthorizationHandler permissionHandler)
 19         {
 20             _permissionHandler = permissionHandler as PermissionHandler;
 21         }
 22         public IActionResult Index()
 23         {
 24             return View();
 25         }
 26 
 27         public IActionResult PermissionAdd()
 28         {           
 29             return View();
 30         }
 31 
 32         [HttpPost("addpermission")]
 33         public IActionResult AddPermission(string url,string userName)
 34         {       
 35             //添加权限
 36             _permissionHandler.UserPermissions.Add(new UserPermission { Url = url, UserName = userName });
 37             return Content("添加成功");
 38         }
 39         
 40         public IActionResult Contact()
 41         {
 42             ViewData["Message"] = "Your contact page.";
 43 
 44             return View();
 45         }
 46 
 47         public IActionResult Error()
 48         {
 49             return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
 50         }
 51         [AllowAnonymous]
 52         [HttpGet("login")]
 53         public IActionResult Login(string returnUrl = null)
 54         {
 55             TempData["returnUrl"] = returnUrl;
 56             return View();
 57         }
 58         [AllowAnonymous]
 59         [HttpPost("login")]
 60         public async Task<IActionResult> Login(string userName, string password, string returnUrl = null)
 61         {
 62             var list = new List<dynamic> {
 63                 new { UserName = "gsw", Password = "111111", Role = "admin",Name="桂素伟",Country="中国",Date="2017-09-02",BirthDay="1979-06-22"},
 64                 new { UserName = "aaa", Password = "222222", Role = "system",Name="测试A" ,Country="美国",Date="2017-09-03",BirthDay="1999-06-22"}
 65             };
 66             var user = list.SingleOrDefault(s => s.UserName == userName && s.Password == password);
 67             if (user != null)
 68             {
 69                 //用户标识
 70                 var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
 71                 identity.AddClaim(new Claim(ClaimTypes.Sid, userName));
 72                 identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
 73                 identity.AddClaim(new Claim(ClaimTypes.Role, user.Role));
 74                 identity.AddClaim(new Claim(ClaimTypes.Country, user.Country));
 75                 identity.AddClaim(new Claim("date", user.Date));
 76 
 77                 await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
 78                 if (returnUrl == null)
 79                 {
 80                     returnUrl = TempData["returnUrl"]?.ToString();
 81                 }
 82                 if (returnUrl != null)
 83                 {
 84                     return Redirect(returnUrl);
 85                 }
 86                 else
 87                 {
 88                     return RedirectToAction(nameof(HomeController.Index), "Home");
 89                 }
 90             }
 91             else
 92             {
 93                 const string badUserNameOrPasswordMessage = "用户名或密码错误!";
 94                 return BadRequest(badUserNameOrPasswordMessage);
 95             }
 96         }
 97         [HttpGet("logout")]
 98         public async Task<IActionResult> Logout()
 99         {
100             await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
101             return RedirectToAction("Index", "Home");
102         }
103         [AllowAnonymous]
104         [HttpGet("denied")]
105         public IActionResult Denied()
106         {
107             return View();
108         }
109     }
110 }

本例设计是当用户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,欢迎大家提出建议,来共同完善这个授权策略。

文章评论

Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
老程序员的下场
老程序员的下场
我是如何打败拖延症的
我是如何打败拖延症的
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
如何成为一名黑客
如何成为一名黑客
总结2014中国互联网十大段子
总结2014中国互联网十大段子
鲜为人知的编程真相
鲜为人知的编程真相
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
Java程序员必看电影
Java程序员必看电影
我的丈夫是个程序员
我的丈夫是个程序员
旅行,写作,编程
旅行,写作,编程
漫画:程序员的工作
漫画:程序员的工作
程序员和编码员之间的区别
程序员和编码员之间的区别
中美印日四国程序员比较
中美印日四国程序员比较
程序员必看的十大电影
程序员必看的十大电影
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
程序员应该关注的一些事儿
程序员应该关注的一些事儿
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
编程语言是女人
编程语言是女人
程序员都该阅读的书
程序员都该阅读的书
程序员的鄙视链
程序员的鄙视链
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
每天工作4小时的程序员
每天工作4小时的程序员
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
那些争议最大的编程观点
那些争议最大的编程观点
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
一个程序员的时间管理
一个程序员的时间管理
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
代码女神横空出世
代码女神横空出世
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
为什么程序员都是夜猫子
为什么程序员都是夜猫子
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有