WCF授权-WCF自定义授权体系详解[实例篇]

  在《WCF授权-WCF自定义授权体系详解[原理篇]》 中,我们谈到WCF自定义授权体系具有两个核心的组件:AuthorizationPolicy和 ServiceAuthorizationManager,已经它们是如何写作最终提供一种基于声明的授权实现。为了让自定义授权有深刻的理解,我们来进 行一个简单实例来演示如何通过自定义这两个组件实现“非角色授权策略”。[源代码从这里下载]

一、创建演示程序解决方案

   我们这个实例依然采用简单的计算服务的例子,并且采用如下图所示的解决方案结构。不过,为了后续的授权策略需要,我们在服务契约ICalculator接 口上定义如下四个分别表示加、减、乘、除的四个运算操作。当然服务类型CalculatorService也进行相应的修正。

WCF授权-WCF自定义授权体系详解[实例篇]

ICalculator:

   1: using System.ServiceModel;
   2: namespace Artech.WcfServices.Contracts
   3: {
   4:     [ServiceContract(Namespace = "http://www.artech.com/")]
   5:     public interface ICalculator
   6:     {
   7:         [OperationContract(Action = "http://www.artech.com/calculator/add")]
   8:         double Add(double x, double y);
   9:         [OperationContract(Action = "http://www.artech.com/calculator/subtract")]
  10:         double Subtract(double x, double y);
  11:         [OperationContract(Action = "http://www.artech.com/calculator/multiply")]
  12:         double Multiply(double x, double y);
  13:         [OperationContract(Action = "http://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: } 

  现在我们的授权策略是这样的:操作Add和Subtract针对仅对用户Foo开放,而Multiply和Divide操作仅对用户Bar开放。 虽然这个简单的授权完全可以通过在相应的服务操作方法上应用PrincipalPermissionAttribute并指定Name属性来实现。但是我 们要尝试通过自定义AuthorizationPolicy和ServiceAuthorizationManager来实现这样的授权策略。先来看看自 定义的AuthorizationPolicy的定义。

二、自定义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 = "http://www.artech.com/calculator/add";

  11:         private const string ActionOfSubtract = "http://www.artech.com/calculator/subtract";

  12:         private const string ActionOfMultiply = "http://www.artech.com/calculator/multiply";

  13:         private const string ActionOfDivide = "http://www.artech.com/calculator/divide";

  14:  

  15:         internal const string ClaimType4AllowedOperation = "http://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方法中,该方法主要的逻辑是这样的:通过EvaluationContext现有的声明集获取当前的用户名(声明类型和声明权限分别为ClaimTypes.Name和Rights.PossessProperty)。针对获取出来的用户名,创建于被授权服务操作关联的声明。其中声明的三要素(类型、权限和资源)分别为:“http://www.artech.com/allowed”、Rights.PossessProperty和操作的Action。最后将这些声明组成一个声明集添加到EvaluationContext中。

三、自定义ServiceAuthorizationManager

  当授权相关的声明集通过自定义的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的属性列表中去获取该安全主体。如果没有,会抛出异常。

四、应用自定义AuthorizationPolicy和ServiceAuthorizationManager

  到目前为止,两个核心的自定义对象(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="http://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="http://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"调用成功...

作者:Artech
出处:http://artech.cnblogs.com/

加支付宝好友偷能量挖...


评论(0)网络
阅读(86)喜欢(0)Asp.Net/C#/WCF