[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, Collection<ServiceEndpoint> endpoints, 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: <?xml version="1.0"?>
2: <configuration>
3: <system.serviceModel>
4: <services>
5: <service name="Artech.WcfServices.Services.CalculatorService" behaviorConfiguration="disableAuthorization">
6: <endpoint address="http://127.0.0.1/calculatorservice" binding="ws2007HttpBinding" contract="Artech.WcfServices.Contracts.ICalculator"/>
7: </service>
8: </services>
9: <behaviors>
10: <serviceBehaviors>
11: <behavior name="disableAuthorization">
12: <serviceAuthorization principalPermissionMode="None"/>
13: </behavior>
14: </serviceBehaviors>
15: </behaviors>
16: </system.serviceModel>
17: </configuration>
而客戶端的服務呼叫程式中,依然是分別以Foo和Bar(Foo具有管理員許可權)的名義進行兩次服服務呼叫。由於兩個Windows帳號許可權的不同,同樣只有第一個服務呼叫能夠成功,這反映在最終的執行結果中。客戶端程式:
1: ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("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<ICalculator>("calculatorService");
9: credential = channelFactory.Credentials.Windows.ClientCredential;
10: credential.UserName = "Bar";
11: credential.Password = "Password";
12: calculator = channelFactory.CreateChannel();
13: Invoke(calculator);
輸出結果:
1: 服務呼叫成功...
2: 服務呼叫失敗...
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的訊息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。
相關文章
- [WCF許可權控制]透過擴充套件自行實現服務授權套件
- [WCF許可權控制]利用WCF自定義授權模式提供當前Principal模式
- [WCF許可權控制]利用WCF自定義授權模式提供當前Principal[原理篇]模式
- 授權許可權服務設計解析
- 擴充套件.Django-許可權系統套件Django
- Django內建許可權擴充套件案例Django套件
- [WCF許可權控制]基於Windows使用者組的授權方式[上篇]Windows
- 微服務中如何設計一個許可權授權服務微服務
- 下載許可權控制機制
- Laravel實現許可權控制Laravel
- 如何用 Vue 實現前端許可權控制(路由許可權 + 檢視許可權 + 請求許可權)Vue前端路由
- 認證鑑權與API許可權控制在微服務架構中的設計與實現:授權碼模式API微服務架構模式
- [許可權擴充套件] Entrust 快取問題套件Rust快取
- 通過 VirtualApp 實現免 Root 許可權 HookAPPHook
- 一對一原始碼,前端頁面許可權和按鈕許可權控制原始碼前端
- 許可權系統:許可權應用服務設計
- 在Windows低許可權下利用服務進行提權Windows
- **CodeIgniter通過hook的方式實現簡單的許可權控制Hook
- 許可權系統:許可權應用服務設計Tu
- PostgreSQL_通過schema控制使用者許可權SQL
- 通過 recompose 實現 react router4 許可權React
- DB2許可權與授權DB2
- 誰用過“擴充套件JAAS實現類例項級授權”裡的例子?請教高手!套件
- django開發之許可權管理(一)——許可權管理詳解(許可權管理原理以及方案)、不使用許可權框架的原始授權方式詳解Django框架
- .NET 程式許可權控制、獲得管理員許可權程式碼
- Solaris下控制ftp的許可權FTP
- 授權物件許可權後的授權者顯示問題物件
- spring aop實現許可權控制,路徑控制Spring
- Laravel-permission(一個許可權管理的擴充套件包) 的使用Laravel套件
- 如何實現使用者通訊授權的可信、可知、可追溯?——通訊授權服務技術解讀
- Elasticsearch 許可權控制Elasticsearch
- Django(63)drf許可權原始碼分析與自定義許可權Django原始碼
- SpringCloud微服務實戰——搭建企業級開發框架(二十八):擴充套件MybatisPlus外掛DataPermissionInterceptor實現資料許可權控制SpringGCCloud微服務框架套件MyBatis
- 前端許可權控制系統的實現思路前端
- Docker安全:通過Docker提升許可權Docker
- 許可權控制下的SQL寫法SQL
- 1.7.6. 授權和撤銷管理許可權
- 為什麼許可權授權很難?- osohq