[WCF許可權控制]利用WCF自定義授權模式提供當前Principal

germo發表於2021-09-09

在《原理篇》中我們談到:如果採用自定義安全主體許可權模式,我們可以透過自定義AuthorizationPolicy或者ServiceAuthorizationManager實現對基於當前認證用於相關的安全主體的提供,進而達到授權的目的。為了讓大家對此有個更加深刻的認識,在這篇文章中我們會提供一個具體的例子。[原始碼從這裡下載]

目錄:      
一、建立自定義AuthorizationPolicy      
二、建立自定義ServiceAuthorizationManager      
三、透過自定義AuthorizationPolicy實現授權      
四、透過自定義ServiceAuthorizationManager實現授權      

一、建立自定義AuthorizationPolicy

我們先來演示透過自定義AuthorizationPolicy以提供當前安全主體的方式。我們透過自定義AuthorizationPolicy實現這樣的授權策略:如果使用者名稱為Foo(假設為管理員),我們建立一個包含“Administrators”角色的安全主體;而對於其他的使用者,提供的安全主體的角色列表中僅僅包括“Guest”。我們為該自定義AuthorizationPolicy起名為SimpleAdministrators,SimpleAdministrators整個定義如下。

   1: public class SimpleAuthorizationPolicy : IAuthorizationPolicy

   

   2: {

   

   3:     public SimpleAuthorizationPolicy()

   

   4:     {

   

   5:         this.Id = Guid.NewGuid().ToString();

   

   6:     }

   

   7:     public bool Evaluate(EvaluationContext evaluationContext, ref object state)

   

   8:     {

   

   9:         string userName = string.Empty;

   

  10:         foreach (ClaimSet claimSet in evaluationContext.ClaimSets)

   

  11:         {

   

  12:             foreach (Claim claim in claimSet.FindClaims(ClaimTypes.Name, Rights.PossessProperty))

   

  13:             {

   

  14:                 userName = (string)claim.Resource;

   

  15:             }

   

  16:         }

   

  17:

   

  18:         if (userName.Contains('\'))

   

  19:         {

   

  20:             userName = userName.Split('\')[1];

   

  21:         }

   

  22:         evaluationContext.Properties["Principal"] = GetPrincipal(userName);

   

  23:         return false;

   

  24:     }

   

  25:

   

  26:     private IPrincipal GetPrincipal(string userName)

   

  27:     {

   

  28:         GenericIdentity identity = new GenericIdentity(userName);

   

  29:         if (string.Compare("Foo", userName, true) == 0)

   

  30:         {

   

  31:             return new GenericPrincipal(identity, new string[] { "Administrators" });

   

  32:         }

   

  33:         return new GenericPrincipal(identity, new string[] {"Guest" });

   

  34:     }

   

  35:

   

  36:     public ClaimSet Issuer

   

  37:     {

   

  38:         get { return ClaimSet.System; }

   

  39:     }

   

  40:     public string Id { get; private set; }

   

  41: }

這個安全主體的提供實現在Evaluate方法中,而其中唯一值得一提的是當前認證使用者名稱的獲取。在客戶端被成功認證之後,被認證的使用者實際上也透過某個宣告(Claim)儲存下來。該宣告的型別為“”,可以透過ClaimTypes的靜態屬性Name得到。而該Claim物件的Resource就是使用者名稱。在得到當前認證使用者名稱之後,相應的GenericPrincipal物件被建立出來,並被置於EvaluationContext的屬性列表中。並且該屬性對應的Key為“Principal”。

二、建立自定義ServiceAuthorizationManager

接下來我們來透過自定義ServiceAuthorizationManager來實現與上面完全一樣的功能,而已授權策略很簡單,我們照例將該自定義ServiceAuthorizationManager起名為SimpleServiceAuthorizationManager。以下是SimpleServiceAuthorizationManager的定義。

   1: public class SimpleServiceAuthorizationManager : ServiceAuthorizationManager

   

   2: {

   

   3:     protected override bool CheckAccessCore(OperationContext operationContext)

   

   4:     {

   

   5:         string userName = operationContext.ServiceSecurityContext.PrimaryIdentity.Name;

   

   6:         if (userName.Contains('\'))

   

   7:         {

   

   8:             userName = userName.Split('\')[1];

   

   9:         }

   

  10:         operationContext.ServiceSecurityContext.AuthorizationContext.Properties["Principal"] = GetPrincipal(userName);

   

  11:         return true;

   

  12:     }

   

  13:     private IPrincipal GetPrincipal(string userName)

   

  14:     {

   

  15:         GenericIdentity identity = new GenericIdentity(userName);

   

  16:         if (string.Compare("Foo", userName, true) == 0)

   

  17:         {

   

  18:             return new GenericPrincipal(identity, new string[] { "Administrators"});

   

  19:         }

   

  20:         return new GenericPrincipal(identity, new string[] { "Guest" });

   

  21:     }

   

  22: }

和自定義AuthorizationPolicy不同的是,認證使用者的獲取在這裡變得更加容易,我們直接可以透過當前ServiceSecurityContext的PrimaryIdentity獲取。需要提醒一下的是,如果你在自定義AuthorizationPolicy的Evaluate方法中呼叫該屬性,會出現一個StackOverflowException異常,因為該屬性的呼叫本身又會觸發Evaluate方法的呼叫。最後被建立的GnericPrincipal被儲存在當前AuthorizationContext的屬性列表中,屬性的Key依然是“Principal”。

三、透過自定義AuthorizationPolicy實現授權

現在我們常見一個例項程式來應用我們建立的自定義AuthorizationPolicy,看看它是否能夠起到我們期望的授權的作用。我們依然沿用我們再熟悉不過的計算服務的例子,解決方案依然按照如下圖所示的結構來設計。整個解決方式包括四個專案:Contracts、Services、Hosting和Client。對於這樣的結構我們已經瞭解得夠多了,在這裡沒有必要再贅言敘述了。

在例項解決方案的整個結構建立之後,我們分別在Contracts和Services專案中定義服務契約介面和服務型別。下面是契約介面ICalculator和服務CalculatorService的定義。而在CalculatorService類的Add方法中應用了PrincipalPermissionAttribute特性,並將Roles屬性設定成了Adminstrators,意味著該服務操作只能被管理員使用者組中的使用者呼叫。

   1: [ServiceContract(Namespace = "")]

   

   2: public interface ICalculator

   

   3: {

   

   4:     [OperationContract]

   

   5:     double Add(double x, double y);

   

   6: }

   

   7:

   

   8: public class CalculatorService : ICalculator

   

   9: {

   

  10:     [PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]

   

  11:     public double Add(double x, double y)

   

  12:     {

   

  13:         return x + y;

   

  14:     }

   

  15: }

現在透過Hosting這個控制檯程式對上面建立的服務進行寄宿。下面給出的是整個寄宿程式的配置,從中我們可以看出:應用到CalculatorService的服務行為列表中包含了PrincipalPermissionMode為Custom的ServiceAuthorizationBehavior。而我們定義的SimpleAuthorizationPolicy型別被配置到了列表中。

   1: 

   

   2: 

   

   3:   

   

   4:     

   

   5:       

   

   6:         

   

   7:       

   

   8:     

   

   9:     

   

  10:       

   

  11:         

   

  12:           

   

  13:               

   

  14:                 

   

  15:               

   

  16:           

   

  17:           

   

  18:         

   

  19:       

   

  20:     

   

  21:   

   

  22: 

由於我們使用了WSHttpBinding,而它在預設的情況下采用Windows客戶端憑證,為此我們需要建立兩個Windows帳號Foo和Bar,密碼被設定為Password。在如下所示的客戶端程式碼中,我們分別以Foo和Bar的名義呼叫了服務。最後將服務能夠成功呼叫的結果列印出來。

   1: class Program

   

   2: {

   

   3:     static void Main(string[] args)

   

   4:     {

   

   5:         ChannelFactory channelFactory = new ChannelFactory("calculatorService");

   

   6:         NetworkCredential credential = channelFactory.Credentials.Windows.ClientCredential;

   

   7:         credential.UserName = "Foo";

   

   8:         credential.Password = "Password";

   

   9:         ICalculator calculator = channelFactory.CreateChannel();

   

  10:         Invoke(calculator);

   

  11:

   

  12:         channelFactory = new ChannelFactory("calculatorService");

   

  13:         credential = channelFactory.Credentials.Windows.ClientCredential;

   

  14:         credential.UserName = "Bar";

   

  15:         credential.Password = "Password";

   

  16:         calculator = channelFactory.CreateChannel();

   

  17:         Invoke(calculator);

   

  18:

   

  19:         Console.Read();

   

  20:     }

   

  21:     static void Invoke(ICalculator calculator)

   

  22:     {

   

  23:         try

   

  24:         {

   

  25:             calculator.Add(1, 2);

   

  26:             Console.WriteLine("服務呼叫成功...");

   

  27:         }

   

  28:         catch (Exception ex)

   

  29:         {

   

  30:             Console.WriteLine("服務呼叫失敗...");

   

  31:         }

   

  32:     }

   

  33: }

從下面的結果來看,只有在使用者名稱為Foo才能成功呼叫服務,而Bar由於許可權不足會導致服務呼叫失敗。這充分證明了透過自定義AuthorizationPolicy能夠正確地起到授權的作用。

   1: 服務呼叫成功...

   

   2: 服務呼叫失敗...


四、透過自定義ServiceAuthorizationManager實現授權

在證明我們自定義的AuthorizationPolicy確實能夠按照我們定義的策略進行授權之後,我們來試試我們自定義的ServiceAuthorizationManager能否同樣完成授權的使命。為此我們唯一需要做的就是改變一下服務寄宿程式的配置。

   1: 

   

   2: 

   

   3:   

   

   4:     

   

   5:       

   

   6:         

   

   7:                   contract="Artech.WcfServices.Contracts.ICalculator"/>

   

   8:       

   

   9:     

   

  10:     

   

  11:       

   

  12:         

   

  13:           

   

  14:                                 serviceAuthorizationManagerType="Artech.WcfServices.Hosting.SimpleServiceAuthorizationManager,

   

  15:                                 Artech.WcfServices.Hosting" >

   

  16:             <!--

   

  17:               

   

  18:             --&gt

   

  19:           

   

  20:           

   

  21:         

   

  22:       

   

  23:     

   

  24:   

   

  25: 

上面所示的採用自定義ServiceAuthorizationManager實現授權的配置。我們將之前新增的AuthorizationPolicy註釋掉,然後透過ServiceAuthorizationBehavior配置節的serviceAuthorizationManagerType屬性設定成我們自定義的SimpleServiceAuthorizationManager的型別。執行程式後,你會得到和上面一樣的輸出結果。

   1: 服務呼叫成功...

   

   2: 服務呼叫失敗...

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1868/viewspace-2810181/,如需轉載,請註明出處,否則將追究法律責任。

相關文章