艾伟_转载:WCF安全之EndPointIdentity

   最近在做一个项目,应用了WCF进行分布式开发,中间还涉及到消息路由器等,好在有WCF提供了强大的基础支持,当然,本身也作了不少的扩展,实际,我 最关心的是WCF的安全问题,网上不少朋友介绍的WCF的安全也是少得可怜,微软发布的WCF Security GUID好像讲得也只是入门级别的教程,离真正应用到项目中还是有很大的距离,这也让我萌发了分享的想法,今天先放出来占个位置吧,有反对的朋友砖头轻 点,呵~,可以告诉你,WCF的安全里,有很多的小秘密,当然还是要告诉你,并且有此小秘密是要自己去体验后才知道,在博客排版方面,李会军(军哥)让人 感觉最舒服,在解说方面,军哥也是以简洁著称,我在这里也学习一下,一起简洁吧,我希望以后的WCF安全探讨里,一次只讲一个小内容好了~

  概述

   Windows Communication Foundation (WCF) 是 Microsoft 为构建面向服务的应用程序而提供的统一编程模型(摘自MSDN),在分布式环境下的安全问题尤为重要,如果你觉得使用了WCF默认的安全措施可以让你高枕 无忧,那明天你可就以回家种田了,当然,对于学习来说,足够了~,但我们讲的是真正的项目应用,WCF在各种协议下的安全提供和保证是不尽相同的。

   背景

 故事发生在一个阳光明媚的下午,一名女子为了混入某小区行窃,将上次偷到的管道维修工作牌别在胸前,当她走近管理员身边时,被管理员一把抓个正着,原来这小区从上次失窃事件后,已经将维修队解散,现在维修都是由管理员联系外部人员,自然也不用别什么工作牌了。

  问题呈现

   1、许多朋友对这个EndPointIdentity相当的不屑顾,千万不要小看它呀,有时候你被wcf弄生弄死的时候还不知道为什么,这次你应该看清楚了。当你新建一个WCF服务类库时,正确的EndPointIdentity声明如下

<endpoint address ="UserData" binding="netTcpBinding" contract="UserService.IUserData" bindingConfiguration="EndpointBinding">
<identity>
     <dns value="localhost"/>
identity>
endpoint>

 说实现,EndPointIdentity这东西在革命初期(wcf初建立时),我觉得它就像是人一盲肠,多了它也没啥用,少了它也不觉得碍事,你不信?删了试试,你要真删除了其实也没什么。

  2、客户端如果引用了服务元数据,生成的EndPointIdentity和服务器端的一模一样,不信你自己看,实际上,你也可以把它删除了(革命初期),对服务调用没啥影响。

  3、在你的绑定中,安全选项为None的时候,你想怎么弄它就怎么弄它,不要紧,随便改。如下

<binding name="EndpointBinding">
   <security mode="None">
      <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign"/>
      <message clientCredentialType="Certificate"/>
   security>
binding>

 4、一旦你将安全策略调整为:Transport(传输安全)

<security mode="Transport">
   <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign"/>
   <message clientCredentialType="Certificate"/>
security>

 5、如果你的服务还是像初期一样没啥改动,仅是启用了传输安全,clientCredentialType的凭据类型也只是windows,没有使用证书,那可以肯定,你的服务没啥问题出现。

6、不过这时候,你有一天不小心的将EndpointIdentity中的dns元素值误删除了1个字 ,我敢肯定,你的恶梦才刚开始,这时候,你再调用服务,将会收到一个异常。

================

未处理 System.ServiceModel.Security.SecurityNegotiationException
  Message="服务器已拒绝客户端凭据。"

=================

非常限的提示,完全找不到任何线索,可能那时你还以为是不小心设置了某种安全策略哩或者是证书什么的没匹配呢。

  正题

IdentityElement类

表示一个配置元素,该配置元素使其他终结点在与该终结点交换消息时可以对其进行身份验证。此类不能被继承。

EndpointIdentity类

一个 abstract 类,实现此类时可提供一个标识,与终结点交换消息的客户端可使用该标识对终结点进行身份验证。

 

1、这两兄弟其实是同一人,起到的作用都是一样,只不过一个是作用于配置文件,一个作用于托管代码中。利用EndpointIdentity来向服务器标识自己是合法的调用,共有6种方式。

(1) 分别是dns、certificate、rsa、servicePrincipalName(spn)、userPrincipalName(upn)、certificateReference(x509证书标识),通常情况下,只采用一种,也是最常用的dns标识方式,当然,我不反对你6种方式一起使用,如何你有需要。

服务建立时默认的标识符为:dns,并且其值为:localhost。

(2)如果你的服务器dns值为localhost,客户端的dns与之不匹配,所抛出的异常描述就是上面的红色字体内容。

(3)如果服务器dns值已被更改(这个更改当然不是你改改配置文件中的值就行的),通常情况下,抛出的异常都会告诉你明确的不匹配,不匹配在哪里,预期是什么,实际是什么,那你改起来就费事了。

2、现在我们再把安全策略调整到消息级别,试试。

Code
//客户端配置
<binding name="EndpointBinding">
          <security mode="Message">
            <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign"/>
            <message clientCredentialType="Certificate"/>
          security>
        binding>
      netTcpBinding>
    bindings>
    <client>
        <endpoint address ="net.tcp://localhost:8799/UserService/UserData"
                          binding="netTcpBinding"
                          contract="Client.References.IUserData"
                          bindingConfiguration="EndpointBinding"
                          behaviorConfiguration="UserDataBehavior">
          <identity>
            <dns value="localhost"/>           
          identity>
        endpoint>
client>  

//服务器配置
<binding name="EndpointBinding">
          <security mode="Message">
            <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign"/>
            <message clientCredentialType="Certificate"/>
          security>
        binding>
      netTcpBinding>
    bindings>
    <services>
      <service name="UserService.UserData" behaviorConfiguration="UserDataBehavior">
        <host>          
          <baseAddresses>
            <add baseAddress = "net.tcp://localhost:8799/UserService" />
          baseAddresses>
        host>
        <endpoint address ="UserData"
                          binding="netTcpBinding"
                          contract="UserService.IUserData"
                          bindingConfiguration="EndpointBinding">
        endpoint>
service> 

看看配置文件,你发现了什么?是的,服务器端的标识被删除,客户端的标识还是dns并且值为localhost,调用服务抛出异常:

==========================

传出消息标识检查失败。所预期的远程终结点的 DNS 标识为“localhost”,但是远程终结点提供的 DNS 请求为“192168168151service”。如果此远程终结点合法,您可以通过在创建通道代理时明确地将 DNS 标识“192168168151service”指定为 EndpointAddress 的“标识”属性来解决此问题。

==========================

在这里,我们忽略了一个事实,当你在服务中将安全策略调整了消息级别安全时,服务必须配置x509证书,正所谓你叫天不应,叫地不灵啊,这时候EndpointIdentity跑出来搞乱了,明明服务器默认是dns标识,值为:loclahost。为什么突然跑出来个“192168168151service”呀?我也很想知道,原来,在服务配置证书后,默认的dns将被替换为证书主题,只要你把dns配置改回来,一切又没问题了。

  新问题

这时候突然冒出来一个新的问题,如果有多个服务器的时候怎么办呀?多个服务器,多半会伴随着路由器的出现(这只是一种假设,与业务有关),我也很想知道,有多个的时候的情况。

  解决它

 答案是通过代码动态创建一个EndpointIdentity,代码比较简单,如下:

UserDataClient client = new UserDataClient();
EndpointIdentity identity = EndpointIdentity.CreateDnsIdentity("192168168151service");
AddressHeaderCollection headers = client.Endpoint.Address.Headers;//如果需要,还可以在这里加入自定义的头消息
Uri uri = client.Endpoint.Address.Uri;
EndpointAddress remoteaddress = new EndpointAddress(uri, identity, headers.ToArray());
UserDataClient newClient = new UserDataClient(client.Endpoint.Binding, remoteaddress);
newClient.ClientCredentials.ClientCertificate.Certificate = client.ClientCredentials.ClientCertificate.Certificate;
client.Abort();//关闭旧通道,当然,这里你还可以用通道工厂创建对象,实际上也很简单
string msg = newClient.GetData(50);

 

===========================================================

 后话

1、实际上,以上的这段代码这里还包括动态的创建地址头的相关信息,声明一个client的好处是可以使用原有的地址头信息(uri,binding等)

2、下篇更精彩,欢迎转载,但请注明出处--梁规晓博客(http://www.cnblogs.com/viter/)!

说得不对的地方,欢迎拍砖!

 

时间: 2024-07-31 02:21:06

艾伟_转载:WCF安全之EndPointIdentity的相关文章

艾伟_转载:WCF版的PetShop之三:实现分布式的Membership和上下文传递

本系列文章导航 WCF版的PetShop之一:PetShop简介 WCF版的PetShop之二:模块中的层次划分 WCF版的PetShop之三:实现分布式的Membership和上下文传递 通过上一篇了解了模块内基本的层次划分之后,接下来我们来聊聊PetShop中一些基本基础功能的实现,以及一些设计.架构上的应用如何同WCF进行集成.本篇讨论两个问题:实现分布式的Membership和客户端到服务端上下文(Context)的传递. 一. 如何实现用户验证 对登录用户的验证是大部分应用所必需的,对

艾伟_转载:WCF基本异常处理模式[中篇]

通过WCF基本的异常处理模式[上篇], 我们知道了:在默认的情况下,服务端在执行某个服务操作时抛出的异常(在这里指非FaultException异常),其相关的错误信息仅仅限于服务端可见,并不会被WCF传递到客户端:如果将开启了IncludeExceptionDetailInFaults的ServiceDebug服务行为通过声明(通过在服务类型上应用ServiceBehaviorAttrite特性)或者配置的方式应用到相应的服务上,异常相关的所有细节信息将会原封不动地向客户端传送. 这两种方式体

艾伟_转载:WCF版的PetShop之一:PetShop简介

本系列文章导航 WCF版的PetShop之一:PetShop简介 WCF版的PetShop之二:模块中的层次划分 WCF版的PetShop之三:实现分布式的Membership和上下文传递 在<WCF技术剖析(卷1)>的最后一章,我写了一个简单基于WCF的Web应用程序,该程序模拟一个最简单的网上订购的场景,所以我将其命名为PetShop.PetShop的目在于让读者体会到在真正的项目开发中,如何正确地.有效地使用WCF.在这个应用中,还会将个人对设计的一些总结融入其中,希望能够对读者有所启发

艾伟_转载:WCF、Net remoting、Web service概念及区别

Windows通信基础(Windows Communication Foundation,WCF)是基于Windows平台下开发和部署服务的软件开发包(Software Development Kit,SDK). WCF就是微软对于分布式处理的 编程技术的集大成者,它将DCOM.Remoting.Web Service.WSE.MSMQ集成在一起,从而降低了分布式系统开发者的学习曲线,并统一了开发标准. WCF是建立在.Net Framework 2.0基础之上的,包含在.NET 3.0/3.5

艾伟_转载:WCF基本异常处理模式[下篇]

从FaultContractAttribute的定义我们可以看出,该特性可以在同一个目标对象上面多次应用(AllowMultiple = true).这也很好理解:对于同一个服务操作,可能具有不同的异常场景,在不同的情况下,需要抛出不同的异常. 1: [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] 2: public sealed class FaultContractAttri

艾伟_转载:[原创]再谈IIS与ASP.NET管道

在2007年9月份,我曾经写了三篇详细介绍IIS架构和ASP.NET运行时管道的文章,深入介绍了IIS 5.x与IIS 6.0HTTP请求的监听与分发机制,以及ASP.NET运行时管道对HTTP请求的处理流程: [原创]ASP.NET Process Model之一:IIS 和 ASP.NET ISAPI[原创]ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline - Part I[原创]ASP.NET Process Model之二:ASP

艾伟_转载:C# Design Patterns (4) - Proxy

本帖介绍 Proxy Pattern (代理模式). Proxy Pattern (代理模式) The Proxy Pattern provides a surrogate or placeholder for another object to control access to it...                                  - Design Patterns: Elements of Reusable Object-Oriented Software 在 Go

艾伟_转载:数组排序方法的性能比较(上):注意事项及试验

昨天有朋友写了一篇文章,其中比较了List的Sort方法与LINQ中排序方法的性能,而最终得到的结果是"LINQ排序方法性能高于List.Sort方法".这个结果不禁让我很疑惑.因为List.Sort方法是改变容器内部元素的顺序,而LINQ排序后得到的是一个新的序列.假如两个排序方法的算法完全一致,LINQ排序也比对方多出元素复制的开销,为什么性能反而会高?如果LINQ排序的算法/实现更为优秀,那为什么.NET Fx不将List.Sort也一并优化一下呢?于是今天我也对这个问题进行了简

艾伟_转载:一个MVC分页Helper

本人写的一个分页Helper,支持普通分页(也就是,首页.上一页.下一页.末页等),综合分页(普通分页和数字分页的综合).下面是分页效果: 分页代码: PagerHelper.cs 代码   1 using System;  2  using System.Collections.Generic;  3 using System.Collections.Specialized;  4 using System.Linq;  5 using System.Web;  6 using System.