zoukankan      html  css  js  c++  java
  • [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 = "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权限控制]WCF自定义授权体系详解[原理篇]
    [WCF权限控制]WCF自定义授权体系详解[实例篇]

  • 相关阅读:
    设置MYSQL允许用IP访问
    EasyUI中那些不容易被发现的坑——EasyUI重复请求2次的问题
    Oracle初级性能优化总结
    Asp.Net MVC3.0网站统计登录认证的在线人数
    App.config和Web.config配置文件的配置节点的解析
    App.config和Web.config配置文件的自定义配置节点
    Asp.Net Web API 2第十八课——Working with Entity Relations in OData
    win7凭据管理、win7多用户远程登录、主机头设置、nuget.org无法访问
    Asp.Net Web API 2第十七课——Creating an OData Endpoint in ASP.NET Web API 2(OData终结点)
    C#基础知识系列八(const和readonly关键字)
  • 原文地址:https://www.cnblogs.com/artech/p/2103445.html
Copyright © 2011-2022 走看看