asp.net core策略授權

来源:http://www.cnblogs.com/axzxs2001/archive/2017/09/06/7482777.html
-Advertisement-
Play Games

在《asp.net core認證與授權》中講解了固定和自定義角色授權系統許可權,其實我們還可以通過其他方式來授權,比如可以通過角色組,用戶名,生日等,但這些主要取決於ClaimTypes,其實我們也可以自定義鍵值來授權,這些統一叫策略授權,其中更強大的是,我們可以自定義授權Handler來達到靈活授權... ...


在《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("/denied");
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不會觸發,性能更優化


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文不進行大量的原理敘述,主要講解實操 實驗環境: 虛擬機:vm Ubuntu:16.04(需要安裝桌面),作為服務端 服務端IP:192.168.193.128 實踐: 1.安裝dhcp服務 apt-get install isc-dhcp-server -y #如果提示E: 無法定位軟體包 is ...
  • 簡單來說內網穿透的目的是:讓外網能訪問你本地的應用,例如在外網打開你本地http://127.0.0.1指向的Web站點。 最近公司的花生殼到期了,要續費,發現價格一直在漲,都是5年以上的老用戶,旗艦版都沒有實現內網完全穿透,打算自己動手替換這個服務,中間走了不少的彎路,這裡記錄一些文字為大家提供參 ...
  • system定義 首先要知道,system函數是c庫中的函數,而不是系統調用。其實system函數使用起來並不複雜,難就難在對其返回值的理解。這個問題,下文會詳細分析。參數的話,很簡單,就是終端的命令即可。這是因為system函數的實現中調用了shell的緣故。 system優缺點 優點: 可以讓c ...
  • 環境:ubuntu16.04 交叉編譯器版本:4.8.3 依賴x264,lame x264: 1.wget ftp://ftp.videolan.org/pub/x264/snapshots/last_stable_x264.tar.bz2 2.tar xvf last_stable_x264.ta ...
  • (green) short pairwise alignment / detailed edit model; (yellow) database search / divergent homology detection; (red) whole genome alignment / alignm ...
  • 參數檢查 跨目錄執行腳本但維持腳本與"."的相對位置不變 彩色日誌輸出 使用臨時文件, 防止使用的文件與已知文件重名而被替換 輸出到標準輸出的同時寫入到文件 逐行處理文本文件,註意文本要set fileformat=unix確保格式正確 ...
  • 添加引用 創建表 創建列 創建行 賦值和取值 篩選行 刪除行 複製表 表排序 ...
  • 前段時間,用CefSharp.WinForms寫了一個可以播放flash以及一些展示頁面的小程式,涉及到跨域訪問之類的問題。CefSharp.WinForms版本49.0.1。 剛開始挺順利,做到播放flash的時候各種黑屏,無法播放。先是回退32那個版本 用NPAPI解決的但是貌似32那個版本在客 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...