[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 = "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"调用成功...


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