[WCF權限控製]WCF自定義授權體係詳解[實例篇]
在《原理篇》中,我們談到WCF自定義授權體係具有兩個核心的組件:AuthorizationPolicy和ServiceAuthorizationManager,已經它們是如何寫作最終提供一種基於聲明的授權實現。為了讓自定義授權有深刻的理解,我們來進行一個簡單實例來演示如何通過自定義這兩個組件實現“非角色授權策略”。[源代碼從這裏下載]
目錄:
一、創建演示程序解決方案
二、自定義AuthorizationPolicy
三、自定義ServiceAuthorizationManager
四、應用自定義AuthorizationPolicy和ServiceAuthorizationManager
。我們這個實例依然采用簡單的計算服務的例子,並且采用如下圖所示的解決方案結構。不過,為了後續的授權策略需要,我們在服務契約ICalculator接口上定義如下四個分別表示加、減、乘、除的四個運算操作。當然服務類型CalculatorService也進行相應的修正。
ICalculator:
1: using System.ServiceModel;
2: namespace Artech.WcfServices.Contracts
3: {
4: [ServiceContract(Namespace = "https://www.artech.com/")]
5: public interface ICalculator
6: {
7: [OperationContract(Action = "https://www.artech.com/calculator/add")]
8: double Add(double x, double y);
9: [OperationContract(Action = "https://www.artech.com/calculator/subtract")]
10: double Subtract(double x, double y);
11: [OperationContract(Action = "https://www.artech.com/calculator/multiply")]
12: double Multiply(double x, double y);
13: [OperationContract(Action = "https://www.artech.com/calculator/divide")]
14: double Divide(double x, double y);
15: }
16: }
CalculatorService:
1: using Artech.WcfServices.Contracts;
2: namespace Artech.WcfServices.Services
3: {
4: public class CalculatorService : ICalculator
5: {
6: public double Add(double x, double y)
7: {
8: return x + y;
9: }
10: public double Subtract(double x, double y)
11: {
12: return x - y;
13: }
14: public double Multiply(double x, double y)
15: {
16: return x * y;
17: }
18: public double Divide(double x, double y)
19: {
20: return x / y;
21: }
22: }
23: }
現在我們的授權策略是這樣的:。雖然這個簡單的授權完全可以通過在相應的服務操作方法上應用PrincipalPermissionAttribute並指定Name屬性來實現。但是我們要嚐試通過自定義AuthorizationPolicy和ServiceAuthorizationManager來實現這樣的授權策略。先來看看自定義的AuthorizationPolicy的定義。
我們將自定義的AuthorizationPolicy創建在Hosting項目中。由於IAuthorizationPolicy定義在System.IdentityModel程序集中,我們先為Hosting項目添加該程序集的引用。由於授權策略比較簡單,我們直接上自定義的AuthorizationPolicy命名為SimpleAuthorizationPolicy,下麵是整個SimpleAuthorizationPolicy的定義。
1: using System;
2: using System.Collections.Generic;
3: using System.IdentityModel.Claims;
4: using System.IdentityModel.Policy;
5: using System.Linq;
6: namespace Artech.WcfServices.Hosting
7: {
8: public class SimpleAuthorizationPolicy : IAuthorizationPolicy
9: {
10: private const string ActionOfAdd = "https://www.artech.com/calculator/add";
11: private const string ActionOfSubtract = "https://www.artech.com/calculator/subtract";
12: private const string ActionOfMultiply = "https://www.artech.com/calculator/multiply";
13: private const string ActionOfDivide = "https://www.artech.com/calculator/divide";
14:
15: internal const string ClaimType4AllowedOperation = "https://www.artech.com/allowed";
16:
17: public SimpleAuthorizationPolicy()
18: {
19: this.Id = Guid.NewGuid().ToString();
20: }
21: public bool Evaluate(EvaluationContext evaluationContext, ref object state)
22: {
23: if (null == state)
24: {
25: state = false;
26: }
27: bool hasAddedClaims = (bool)state;
28: if (hasAddedClaims)
29: {
30: return true; ;
31: }
32: IList<Claim> claims = new List<Claim>();
33: foreach (ClaimSet claimSet in evaluationContext.ClaimSets)
34: {
35: foreach (Claim claim in claimSet.FindClaims(ClaimTypes.Name, Rights.PossessProperty))
36: {
37: string userName = (string)claim.Resource;
38: if (userName.Contains('\\'))
39: {
40: userName = userName.Split('\\')[1];
41: if (string.Compare("Foo", userName, true) == 0)
42: {
43: claims.Add(new Claim(ClaimType4AllowedOperation,ActionOfAdd, Rights.PossessProperty));
44: claims.Add(new Claim(ClaimType4AllowedOperation, ActionOfSubtract, Rights.PossessProperty));
45: }
46: if (string.Compare("Bar", userName, true) == 0)
47: {
48: claims.Add(new Claim(ClaimType4AllowedOperation,ActionOfMultiply, Rights.PossessProperty));
49: claims.Add(new Claim(ClaimType4AllowedOperation, ActionOfDivide, Rights.PossessProperty));
50: }
51: }
52: }
53: }
54: evaluationContext.AddClaimSet(this, new DefaultClaimSet(this.Issuer, claims));
55: state = true;
56: return true;
57: }
58: public ClaimSet Issuer
59: {
60: get { return ClaimSet.System; }
61: }
62: public string Id { get; private set; }
63: }
64: }
我們主要來介紹Evaluate方法中,該方法主要的邏輯是這樣的:。其中聲明的三要素(類型、權限和資源)分別為:“https://www.artech.com/allowed”、Rights.PossessProperty和操作的Action。最後將這些聲明組成一個聲明集添加到EvaluationContext中。
當授權相關的聲明集通過自定義的AuthorizationPolicy被初始化之後,我們通過自定義ServiceAuthorizationManager來分析這些聲明,並作做出當前操作是否被授權調用的最終判斷。類似於SimpleAuthorizationPolicy,我們將自定義的ServiceAuthorizationManager起名為SimpleServiceAuthorizationManager,同樣定義於Hosting項目中,下麵是整個SimpleServiceAuthorizationManager類型的定義。
1: using System.IdentityModel.Claims;
2: using System.Security.Principal;
3: using System.ServiceModel;
4: namespace Artech.WcfServices.Hosting
5: {
6: public class SimpleServiceAuthorizationManager : ServiceAuthorizationManager
7: {
8: protected override bool CheckAccessCore(OperationContext operationContext)
9: {
10: string action = operationContext.RequestContext.RequestMessage.Headers.Action;
11: foreach (ClaimSet claimSet in operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets)
12: {
13: if (claimSet.Issuer == ClaimSet.System)
14: {
15: foreach (Claim c in claimSet.FindClaims(SimpleAuthorizationPolicy.ClaimType4AllowedOperation, Rights.PossessProperty))
16: {
17: if (action == c.Resource.ToString())
18: {
19: GenericIdentity identity = new GenericIdentity("");
20: operationContext.ServiceSecurityContext.AuthorizationContext.Properties["Principal"] =
21: new GenericPrincipal(identity, null);
22: return true;
23: }
24: }
25: }
26: }
27: return false;
28: }
29: }
30: }
由於基於被授權操作的聲明已經通過SimpleAuthorizationPolicy被成功添加到EvaluationContext的聲明集列表,並最終作為當前AuthorizationContext聲明集的一部分。那麼,。這樣的邏輯實現在重寫的CheckAccessCore方法中。此外,還有一點需要注意的是:在做出成功授權的情況下,需要設置當前的安全主體,因為不管這個安全主體是否需要,WCF總是會試圖從當前AuthorizationContext的屬性列表中去獲取該安全主體。如果沒有,會拋出異常。
到目前為止,兩個核心的自定義對象(SimpleAuthorizationPolicy和SimpleServiceAuthorizationManager)都已經創建好了,我們現在通過配置的方式將它們設置到應用到服務的ServiceAuthorizationBehavior服務行為上。下麵兩段XML片斷分別表示服務寄宿和客戶端的配置。
服務寄宿配置:
1: <?xml version="1.0"?>
2: <configuration>
3: <system.serviceModel>
4: <services>
5: <service name="Artech.WcfServices.Services.CalculatorService" behaviorConfiguration="useCustomAuthorization">
6: <endpoint address="https://127.0.0.1/calculatorservice" binding="ws2007HttpBinding"
contract="Artech.WcfServices.Contracts.ICalculator"/>
7: </service>
8: </services>
9: <behaviors>
10: <serviceBehaviors>
11: <behavior name="useCustomAuthorization">
12: <serviceAuthorization principalPermissionMode="Custom"
serviceAuthorizationManagerType="Artech.WcfServices.Hosting.SimpleServiceAuthorizationManager,Artech.WcfServices.Hosting">
13: <authorizationPolicies >
14: <add policyType="Artech.WcfServices.Hosting.SimpleAuthorizationPolicy, Artech.WcfServices.Hosting" />
15: </authorizationPolicies>
16: </serviceAuthorization>
17: <serviceDebug includeExceptionDetailInFaults="true"/>
18: </behavior>
19: </serviceBehaviors>
20: </behaviors>
21: </system.serviceModel>
22: </configuration>
客戶端配置:
1: <?xml version="1.0"?>
2: <configuration>
3: <system.serviceModel>
4: <client>
5: <endpoint name="calculatorService" address="https://127.0.0.1/calculatorservice" binding="ws2007HttpBinding"
contract="Artech.WcfServices.Contracts.ICalculator"/>
6: </client>
7: </system.serviceModel>
8: </configuration>
我們最終需要驗證的WCF是否能夠按照我們自定義的策略進行授權。為了演示方便,我創建了如下一個名稱為Invoke的輔助方法。Invoke方法的三個參數分別代表進行服務調用的委托、服務代理對象和操作名稱。服務操作調用會在該方法中執行,並最終輸出相應的文字表示服務調用是否成功。
1: static void Invoke(Action<ICalculator> action, ICalculator proxy, string operation)
2: {
3: try
4: {
5: action(proxy);
6: Console.WriteLine("服務操作\"{0}\"調用成功...", operation);
7: }
8: catch (Exception ex)
9: {
10: Console.WriteLine("服務操作\"{0}\"調用失敗...", operation);
11: }
12: }
在如下的代碼中,我們分別以用戶名Foo和Bar的名義通過上麵的Invoke輔助方法對計算服務的四個操作進行訪問。而程序執行的最終結果是和我們自定義的授權策略是一致的:用戶Foo僅僅授予了調用Add和Substract操作的權限,而其餘兩個授權給用戶Bar。
1: static void Main(string[] args)
2: {
3: ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorService");
4: NetworkCredential credential = channelFactory.Credentials.Windows.ClientCredential;
5: credential.UserName = "Foo";
6: credential.Password = "Password";
7: ICalculator calculator = channelFactory.CreateChannel();
8: Invoke(proxy => proxy.Add(1, 2), calculator, "Add");
9: Invoke(proxy => proxy.Subtract(1, 2), calculator, "Subtract");
10: Invoke(proxy => proxy.Multiply(1, 2), calculator, "Multiply");
11: Invoke(proxy => proxy.Divide(1, 2), calculator, "Divide");
12: Console.WriteLine();
13:
14: channelFactory = new ChannelFactory<ICalculator>("calculatorService");
15: credential = channelFactory.Credentials.Windows.ClientCredential;
16: credential.UserName = "Bar";
17: credential.Password = "Password";
18: calculator = channelFactory.CreateChannel();
19: Invoke(proxy => proxy.Add(1, 2), calculator, "Add");
20: Invoke(proxy => proxy.Subtract(1, 2), calculator, "Subtract");
21: Invoke(proxy => proxy.Multiply(1, 2), calculator, "Multiply");
22: Invoke(proxy => proxy.Divide(1, 2), calculator, "Divide");
23:
24: Console.Read();
25: }
輸出結果:
1: 服務操作"Add"調用成功...
2: 服務操作"Subtract"調用成功...
3: 服務操作"Multiply"調用失敗...
4: 服務操作"Divide"調用失敗...
5:
6: 服務操作"Add"調用失敗...
7: 服務操作"Subtract"調用失敗...
8: 服務操作"Multiply"調用成功...
9: 服務操作"Divide"調用成功...
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-26 16:05:12