[WCF权限控制]基于Windows用户组的授权方式[下篇]

为了让读者对基于Windows用户组的授权具有深刻的认识,接下来我们通过一个简单的事例来讲解在真正的应用中该授权模式如何使用。对于接下来演示的事例,我们将采用Windows认证和授权。至于授权的最终实现,我们采用的是在服务方法上面应用PrincipalPermissionAttribute特性方式的声明式授权。[源代码从这里下载]

目录:
步骤一、创建测试帐号
步骤二、创建服务契约和服务
步骤三、寄宿服务
步骤四、创建客户端程序

步骤一、创建测试帐号

在创建事例解决方案之前我们先完成相应的准备工作,创建两个测试用的Windows帐号。假设两个帐号的名称分别为Foo和Bar,密码为Password。然后将帐号Foo添加到管理员(Administrators)用户组中

步骤二、创建服务契约和服务

我们依然沿用我们再熟悉不过的计算服务的例子,解决方案依然按照如下图所示的结构来设计。整个解决方式包括四个项目:Contracts、Services、Hosting和Client。对于这样的结构我们已经了解得够多了,在这里没有必要再赘言叙述了。

[WCF权限控制]基于Windows用户组的授权方式[下篇]

在实例解决方案的整个结构建立之后,我们分别在Contracts和Services项目中定义服务契约接口和服务类型。下面是契约接口ICalculator和服务CalculatorService的定义。而在CalculatorService类的Add方法中应用了PrincipalPermissionAttribute特性,并将Roles属性设置成了Adminstrators,意味着该服务操作只能被管理员用户组中的用户调用。

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]
   8:         double Add(double x, double y);
   9:     }   
  10: }

CalculatorService:

   1: using System.Security.Permissions;
   2: using Artech.WcfServices.Contracts;
   3: namespace Artech.WcfServices.Services
   4: {   
   5:     public class CalculatorService : ICalculator
   6:     {
   7:         [PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
   8:         public double Add(double x, double y)
   9:         {           
  10:             return x + y;
  11:         }
  12:     }
  13: } 

步骤三、寄宿服务

现在通过Hosting这个控制台程序对上面创建的服务进行寄宿。下面给出的是整个寄宿程序的配置。从该配置我们可以看到,服务唯一的终结点采用的绑定类型为WS2007HttpBinding。而在默认的情况下,WS2007HttpBinding采用Message安全模式和Windows认证方式。此外,基于UseWindowsGroups安全主体权限模式的ServiceAuthorization服务行为被应用到了该服务上。

   1: <?xml version="1.0"?>
   2: <configuration>
   3:   <system.serviceModel>    
   4:     <services>
   5:       <service name="Artech.WcfServices.Services.CalculatorService" behaviorConfiguration="useWindowsGroupsAuthorization">
   6:         <endpoint address="http://127.0.0.1/calculatorservice" binding="ws2007HttpBinding" 
   7: contract="Artech.WcfServices.Contracts.ICalculator"/>
   8:       </service>
   9:     </services>
  10:     <behaviors>
  11:       <serviceBehaviors>
  12:         <behavior  name="useWindowsGroupsAuthorization">
  13:           <serviceAuthorization principalPermissionMode="UseWindowsGroups"/>
  14:         </behavior>
  15:       </serviceBehaviors>
  16:     </behaviors>
  17:   </system.serviceModel>
  18: </configuration>

而服务寄宿的程序依然简洁如故,仅仅包括正对寄宿服务类型的ServiceHost的创建和开启而已。

   1: using System.ServiceModel;
   2: using Artech.WcfServices.Services;
   3: using System;
   4: namespace Artech.WcfServices.Hosting
   5: {
   6:     public class Program
   7:     {
   8:         static void Main(string[] args)
   9:         {
  10:             using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
  11:             {
  12:                 host.Open();
  13:                 Console.Read();
  14:             }
  15:         }
  16:       }
  17:   }

步骤四、创建客户端程序

来到整个实例的最后一个步骤,我们将服务调用的客户程序定义在Client项目中。整个实例演示的目的在于确认针对服务操作Add的授权根据Windows用户组进行的,我们只需要关注被授权的服务操作是否被成功调用。为此,我写了如下一个简单的辅助性的方法Invoke。如果服务操作被成功执行,输出“服务调用成功”,如果抛出异常则输出“服务调用失败”。

   1: static void Invoke(ICalculator calculator)
   2: {
   3:     try
   4:     {
   5:         calculator.Add(1,2);
   6:         Console.WriteLine("服务调用成功...");
   7:     }
   8:     catch (Exception ex)
   9:     {
  10:         Console.WriteLine("服务调用失败...");
  11:     }
  12: }

下面演示了完整的客户端程序和响应的配置。整个程序体现了两次针对相同服务操作的调用,而两次服务调用采用的客户端凭证分别是基于之前创建的两个Windows帐号Foo和Bar。

客户端程序:

   1: using System.Net;
   2: using System.ServiceModel;
   3: using Artech.WcfServices.Contracts;
   4: namespace Artech.WcfServices.Clients
   5: {
   6:     class Program
   7:     {
   8:         static void Main(string[] args)
   9:         {
  10:  
  11:             ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorService");
  12:             NetworkCredential credential = channelFactory.Credentials.Windows.ClientCredential;
  13:             credential.UserName = "Foo";
  14:             credential.Password = "Password";
  15:             ICalculator calculator = channelFactory.CreateChannel();
  16:             Invoke(calculator);
  17:  
  18:             channelFactory = new ChannelFactory<ICalculator>("calculatorService");
  19:             credential = channelFactory.Credentials.Windows.ClientCredential;
  20:             credential.UserName = "Bar";
  21:             credential.Password = "Password";
  22:             calculator = channelFactory.CreateChannel();
  23:             Invoke(calculator);
  24:  
  25:             Console.Read();
  26:         }
  27:  
  28:     }
  29: }

配置:

   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" 
   6: contract="Artech.WcfServices.Contracts.ICalculator"/>
   7:     </client>   
   8:   </system.serviceModel>
   9: </configuration>

由于调用的服务操作需要具有管理员权限采用调用,所以以Foo名义进行调用是没有为题的,但是对于帐号Bar,由于权限不足将会调用失败。而客户端输出的结果反映了这一点。

   1: 服务调用成功...
   2: 服务调用失败...

: 对于这个事例演示来说,服务操作只有具有管理员权限方能被正常调用。虽然我们创建的Windows帐号Foo在管理员用户组中,但是如果你使用Vista、Windows Server 2008和Windows 7这三种操作系统,在UAC开启的情况下,即使你以管理员运行我们的演示程序,Foo也不具有管理员权限。所以,你需要关闭UAC采用得到正确的执行结果,否则两次调用都是输出“服务调用失败...”。

[WCF权限控制]基于Windows用户组的授权方式[上篇]
[WCF权限控制]基于Windows用户组的授权方式[下篇]


作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
上一篇:程序员数学基础【七、等比数列 棋盘麦粒】


下一篇:高效能程序员的修炼札记:安全基础,保护用户数据