asp.Netcoreポリシーライセンス

60997 ワード

「asp.net core認証と授権」では、固定とカスタムロール授権システムの権限について説明しています.実際には、ロールグループ、ユーザー名、誕生日など、他の方法で授権することもできますが、これらは主にClaimTypeに依存しています.実際には、カスタムキー値で授権することもできます.これらはポリシー授権と呼ばれています.その中で、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()は、ログイン時に認証に成功した後、適切なClaimsを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 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 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 /// 
 2     ///     
 3     /// 
 4     public class UserPermission
 5     {
 6         /// 
 7         ///    
 8         /// 
 9         public string UserName
10         { get; set; }
11         /// 
12         ///   Url
13         /// 
14         public string Url
15         { get; set; }
16     }

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

 1 /// 
 2     ///      
 3     /// 
 4     public class PermissionRequirement : IAuthorizationRequirement
 5     {
 6         /// 
 7         ///       
 8         /// 
 9         public  List UserPermissions { get;private set; }
10         /// 
11         ///    action
12         /// 
13         public string DeniedAction { get; set; }
14         /// 
15         ///   
16         /// 
17         ///    action
18         ///       
19         public PermissionRequirement(string deniedAction, List userPermissions)
20         {
21             DeniedAction = deniedAction;
22             UserPermissions = userPermissions;
23         }
24     }

再定义自定义授权Hanlder,我们命名为PermissionHandler,此类必需继承AuthorizationHandler,只用实现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     /// 
10     ///     Handler
11     /// 
12     public class PermissionHandler : AuthorizationHandler
13     {
14         /// 
15         ///     
16         /// 
17         public List 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 {
 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();
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 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 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を使ってもいいです.次に2つの方法の優劣を比較します.
 
ミドルウェア
カスタムライセンスHandler
ユーザー権限セット
静的オブジェクト
オブジェクトをソリッド化
ホットアップデート時
ミドルウェア名でユーザー権限セットの更新
スタートアップだからcsではPermissionHandlerは注放に依存しており,熱的に更新された構造で取得して操作できる.
パフォーマンス
各アクションリクエストはInvockメソッドをトリガーし、[AllowAnonymous]プロパティをマークしたActionもトリガーします.
このメソッドは[Authorize]プロパティのアクションのみがトリガーされ、[AllowAnonymous]プロパティのアクションはトリガーされず、パフォーマンスが最適化されます.
 
最後に、ライセンスポリシーをNuGetのパッケージにしました.asp.Netcore 2.0のプロジェクトでAuthorizePolicyがこのパッケージを参照すると、パッケージに対応するgithubアドレスが表示されます.https://github.com/axzxs2001/AuthorizePolicyこの授権戦略を共同で改善するための提案を歓迎します.