[WCF許可權控制]透過擴充套件自行實現服務授權
其實針對安全主體的授權實現的原理很簡單,原則上講,只要你能在服務操作執行之前能夠根據本認證的使用者正確設定當前的安全主體就可以了。如果你瞭解WCF的整個執行時框架結構,你會馬上想到用於授權的安全主體初始化可以透過自定義CallContextInitializer來實現。[原始碼從這裡下載]
目錄:
CallContextInitializer簡介
步驟一、自定義CallContextInitializer
步驟二、建立服務行為
步驟三、使用服務行為進行授權
CallContextInitializer簡介
對於WCF的整個執行時框架來說,CallContextInitializer是一個重要的物件。一個執行時服務操作(DispatchOperation)具有一個CallContextInitializer列表。而每一個CallContextInitializer實現ICallContextInitializer介面。如下面的程式碼片斷所示,ICallContextInitializer具有兩個方法BeforeInvoke和AfterInvoke。它們分別在操作方法之前前後進行呼叫上下文的初始化和清理操作。那麼我麼就可以自定義CallContextInitializer,在BeforeInvoke中初始化當前的安全主體。
1: public interface ICallContextInitializer
2: {
3: void AfterInvoke(object correlationState);
4: object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message);
5: }
步驟一、自定義CallContextInitializer
我們授權自定義一個抽象的CallContextInitializer,起名為AuthorizationCallContextInitializerBase。下面的程式碼片斷給出了AuthorizationCallContextInitializerBase的整個定義。AuthorizationCallContextInitializerBase具有一個抽象的方法GetPrincipal用於根據當前的安全上下文資訊建立安全主體。該方法會在BeforeInvoke方法被呼叫,返回值被設定成當前執行緒的安全主體。為了讓服務操作執行之後當前執行緒的上下文恢復到執行前的狀態,在BeforeInvoke方法中當前的安全主體被儲存下來,並傳遞給AfterInvoke方法中恢復當前執行緒的原來的安全主體。
1: public abstract class AuthorizationCallContextInitializerBase: ICallContextInitializer
2: {
3: public void AfterInvoke(object correlationState)
4: {
5: IPrincipal principal = correlationState as IPrincipal;
6: if (null != principal)
7: {
8: Thread.CurrentPrincipal = principal;
9: }
10: }
11: public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
12: {
13: var originalPrincipal = Thread.CurrentPrincipal;
14: Thread.CurrentPrincipal = this.GetPrincipal(ServiceSecurityContext.Current);
15: return originalPrincipal;
16: }
17: protected abstract IPrincipal GetPrincipal(ServiceSecurityContext serviceSecurityContext);
18: }
基於兩種安全主體許可權模式,我們建立了兩個具體的CallContextInitializer。第一個為基於Windows使用者組的WindowsAuthorizationCallContextInitializer。WindowsAuthorizationCallContextInitializer定義如下,它繼承了AuthorizationCallContextInitializerBase,在實現的抽象方法GetPrincipal中根據當前ServiceSecurityContext的WindowsIdentity屬性建立WindowsPrincipal。
1: public class WindowsAuthorizationCallContextInitializer:AuthorizationCallContextInitializerBase
2: {
3: protected override IPrincipal GetPrincipal(ServiceSecurityContext serviceSecurityContext)
4: {
5: WindowsIdentity identity = serviceSecurityContext.WindowsIdentity;
6: if (null == identity)
7: {
8: identity =WindowsIdentity.GetAnonymous();
9: }
10: return new WindowsPrincipal(identity);
11: }
12: }
而基於ASP.NET Roles安全主體許可權模式的安全主體初始化實現在如下所示的AspRoleAuthorizationCallContextInitializer類中。AspRoleAuthorizationCallContextInitializer具有一個RoleProvider屬性,表示用於獲取當前使用者角色列表的RoleProvider,該屬性在建構函式中被初始化。在實現的GetPrincipal抽象方法中,藉助於RoleProvider獲取基於當前使用者的所有角色,並建立GenericPrincipal。
1: public class AspRoleAuthorizationCallContextInitializer : AuthorizationCallContextInitializerBase
2: {
3: public RoleProvider RoleProvider { get; private set; }
4: public AspRoleAuthorizationCallContextInitializer(RoleProvider roleProvider)
5: {
6: this.RoleProvider = roleProvider;
7: }
8: protected override IPrincipal GetPrincipal(ServiceSecurityContext serviceSecurityContext)
9: {
10: var userName = serviceSecurityContext.PrimaryIdentity.Name;
11: var identity = new GenericIdentity(userName);
12: var roles = this.RoleProvider.GetRolesForUser(userName);
13: return new GenericPrincipal(identity, roles);
14: }
15: }
步驟二、建立服務行為
現在,使用者進行安全主體初始化的兩個具體的CallContextInitializer已經建立完成,現在需要做的工作就是將其應用到WCF的執行時框架體系之中。為此,我們建立瞭如下一個服務行為ServiceAuthorizationBehaviorAttribute。ServiceAuthorizationBehaviorAttribute是一個自定義特性,並實現了IServiceBehavior介面。它具有兩個兩個屬性:PrincipalPermissionMode和CallContextInitializer。前者在建構函式中指定,我們根據該引數決定具體建立的CallContextInitializer型別,是WindowsAuthorizationCallContextInitializer還是AspRoleAuthorizationCallContextInitializer。而建構函式中具有一個可選的引數roleProviderName表示採用的RoleProvider配置名稱。
1: [AttributeUsage( AttributeTargets.Class)]
2: public class ServiceAuthorizationBehaviorAttribute: Attribute, IServiceBehavior
3: {
4: public PrincipalPermissionMode PrincipalPermissionMode { get; private set; }
5: public ICallContextInitializer CallContextInitializer { get; private set; }
6:
7: public ServiceAuthorizationBehaviorAttribute(PrincipalPermissionMode principalPermissionMode, string roleProviderName = "")
8: {
9: switch (principalPermissionMode)
10: {
11: case PrincipalPermissionMode.UseWindowsGroups:
12: {
13: this.CallContextInitializer = new WindowsAuthorizationCallContextInitializer();
14: break;
15: }
16: case PrincipalPermissionMode.UseAspNetRoles:
17: {
18: if (string.IsNullOrEmpty(roleProviderName))
19: {
20: this.CallContextInitializer = new AspRoleAuthorizationCallContextInitializer(Roles.Provider);
21: }
22: else
23: {
24: this.CallContextInitializer = new AspRoleAuthorizationCallContextInitializer(Roles.Providers[roleProviderName]);
25: }
26: break;
27: }
28: case PrincipalPermissionMode.Custom:
29: {
30: throw new ArgumentException("只有UseWindowsGroups和UseAspNetRoles模式被支援!");
31: }
32: }
33: }
34:
35: public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collectionendpoints, BindingParameterCollection bindingParameters) { }
36:
37: public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
38: {
39: if (null == this.CallContextInitializer)
40: {
41: return;
42: }
43:
44: foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
45: {
46: foreach (EndpointDispatcher endpoint in channelDispatcher.Endpoints)
47: {
48: foreach (DispatchOperation operation in endpoint.DispatchRuntime.Operations)
49: {
50: operation.CallContextInitializers.Add(this.CallContextInitializer);
51: }
52: }
53: }
54: }
55: public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
56: }
CallContextInitializer的註冊實現在ApplyDispatchBehavior方法中,邏輯很簡單:遍歷所有通道分發器(ChannelDispatcher),每個通道分發器的所有終結點分發器(EndpointDispatcher),以及每個終結點分發器對應的分發執行時(DispatchRuntime)的所有執行時操作(DispatchOperation)。最後將初始化的CallContextInitializer新增到操作的CallContextInitializer列表中。
步驟三、使用服務行為進行授權
由於上面定義的服務行為ServiceAuthorizationBehaviorAttribute是一個自定義特性,所以我們可以直接將其應用到服務型別上。我們直接採用《基於Windows使用者組的授權方式[下篇]》的例子。如下所示,在服務型別CalculatorService上應用了ServiceAuthorizationBehaviorAttribute特性,並採用了UseWindowsGroups安全主體許可權模式。
1: [ServiceAuthorizationBehavior(PrincipalPermissionMode.UseWindowsGroups)]
2: public class CalculatorService : ICalculator
3: {
4: [PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
5: public double Add(double x, double y)
6: {
7: return x + y;
8: }
9: }
為了證明我們自定義的服務行為也能和ServiceAuthorizationBehavior一樣實現正確的授權,我們需要將ServiceAuthorizationBehavior的授權功能關閉。為此我們修正了服務端的配置,將ServiceAuthorizationBehavior的PrincipalPermissionMode設定為None。
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
而客戶端的服務呼叫程式中,依然是分別以Foo和Bar(Foo具有管理員許可權)的名義進行兩次服服務呼叫。由於兩個Windows帳號許可權的不同,同樣只有第一個服務呼叫能夠成功,這反映在最終的執行結果中。客戶端程式:
1: ChannelFactorychannelFactory = new ChannelFactory ("calculatorService");
2: NetworkCredential credential = channelFactory.Credentials.Windows.ClientCredential;
3: credential.UserName = "Foo";
4: credential.Password = "Password";
5: ICalculator calculator = channelFactory.CreateChannel();
6: Invoke(calculator);
7:
8: channelFactory = new ChannelFactory("calculatorService");
9: credential = channelFactory.Credentials.Windows.ClientCredential;
10: credential.UserName = "Bar";
11: credential.Password = "Password";
12: calculator = channelFactory.CreateChannel();
13: Invoke(calculator);
輸出結果:
1: 服務呼叫成功...
2: 服務呼叫失敗...
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2524/viewspace-2810180/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- [WCF許可權控制]利用WCF自定義授權模式提供當前Principal模式
- 授權許可權服務設計解析
- Django內建許可權擴充套件案例Django套件
- 擴充套件.Django-許可權系統套件Django
- Laravel實現許可權控制Laravel
- 如何用 Vue 實現前端許可權控制(路由許可權 + 檢視許可權 + 請求許可權)Vue前端路由
- 微服務中如何設計一個許可權授權服務微服務
- 許可權控制
- 許可權系統:許可權應用服務設計
- 報表如何透過引數控制資料許可權
- autohotkey透過com物件控制excel的許可權問題物件Excel
- 認證鑑權與API許可權控制在微服務架構中的設計與實現:授權碼模式API微服務架構模式
- SpringCloud微服務實戰——搭建企業級開發框架(二十八):擴充套件MybatisPlus外掛DataPermissionInterceptor實現資料許可權控制SpringGCCloud微服務框架套件MyBatis
- 許可權系統:許可權應用服務設計Tu
- Laravel-permission(一個許可權管理的擴充套件包) 的使用Laravel套件
- 授權|取消授權MYSQL資料庫使用者許可權MySql資料庫
- shiro許可權控制
- SpringBoot(一) 如何實現AOP的許可權控制Spring Boot
- 前端許可權控制系統的實現思路前端
- Spring security(五)-完美許可權管理系統(授權過程分析)Spring
- 7.4 透過API列舉程式許可權API
- Laravel5.1基於Entrust擴充套件包實現的RBAC許可權控制模組(遷移到其他專案中的方法)LaravelRust套件
- 許可權管理之多租戶隔離授權
- 1.7.6. 授權和撤銷管理許可權
- 為什麼許可權授權很難?- osohq
- Linux的許可權控制Linux
- spring aop實現簡單的許可權控制功能Spring
- Atlas 2.1.0 實踐(4)—— 許可權控制
- 通過 VirtualApp 實現免 Root 許可權 HookAPPHook
- django開發之許可權管理(一)——許可權管理詳解(許可權管理原理以及方案)、不使用許可權框架的原始授權方式詳解Django框架
- Think Authz:支援 ACL、RBAC、ABAC 等模型的授權(角色和許可權控制)庫模型
- springboot + shiro 實現登入認證和許可權控制Spring Boot
- Spring Security實現統一登入與許可權控制Spring
- springcloud-gateway整合jwt+jcasbin實現許可權控制SpringGCCloudGatewayJWT
- 在Windows低許可權下利用服務進行提權Windows
- 通過 recompose 實現 react router4 許可權React
- Nestjs RBAC 許可權控制管理實踐(一)JS
- Nestjs RBAC 許可權控制管理實踐 (二)JS