閱讀553 返回首頁    go 阿裏雲 go 技術社區[雲棲]


[WCF權限控製]WCF自定義授權體係詳解[實例篇]

在《原理篇》中,我們談到WCF自定義授權體係具有兩個核心的組件:AuthorizationPolicy和ServiceAuthorizationManager,已經它們是如何寫作最終提供一種基於聲明的授權實現。為了讓自定義授權有深刻的理解,我們來進行一個簡單實例來演示如何通過自定義這兩個組件實現“非角色授權策略”。[源代碼從這裏下載]

目錄:
一、創建演示程序解決方案
二、自定義AuthorizationPolicy
三、自定義ServiceAuthorizationManager
四、應用自定義AuthorizationPolicy和ServiceAuthorizationManager

。我們這個實例依然采用簡單的計算服務的例子,並且采用如下圖所示的解決方案結構。不過,為了後續的授權策略需要,我們在服務契約ICalculator接口上定義如下四個分別表示加、減、乘、除的四個運算操作。當然服務類型CalculatorService也進行相應的修正。

image

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"調用成功...

[WCF權限控製]WCF自定義授權體係詳解[原理篇]
[WCF權限控製]WCF自定義授權體係詳解[實例篇]


作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-26 16:05:12

  上一篇:go  CentOS7安裝與配置ntpd服務
  下一篇:go  CC自定義防護驗證最佳實踐