艾伟: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-09-17 04:25:07

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

艾伟_转载:WCF安全之EndPointIdentity

最近在做一个项目,应用了WCF进行分布式开发,中间还涉及到消息路由器等,好在有WCF提供了强大的基础支持,当然,本身也作了不少的扩展,实际,我 最关心的是WCF的安全问题,网上不少朋友介绍的WCF的安全也是少得可怜,微软发布的WCF Security GUID好像讲得也只是入门级别的教程,离真正应用到项目中还是有很大的距离,这也让我萌发了分享的想法,今天先放出来占个位置吧,有反对的朋友砖头轻 点,呵~,可以告诉你,WCF的安全里,有很多的小秘密,当然还是要告诉你,并且有此小秘密是要自己去体验后才

WCF安全之EndPointIdentity

最近在做一个项目,应用了WCF进行分布式开发,中间还涉及到消息路由器等 ,好在有WCF提供了强大的基础支持,当然,本身也作了不少的扩展,实际,我 最关心的是WCF的安全问题,网上不少朋友介绍的WCF的安全也是少得可怜,微软 发布的WCF Security GUID好像讲得也只是入门级别的教程,离真正应用到项目 中还是有很大的距离,这也让我萌发了分享的想法,今天先放出来占个位置吧,有反对的朋友砖头轻 点,呵~,可以告诉你,WCF的安全里,有很多的小秘密,当然还是要告诉你,并且有此小秘密是要自己去体

艾伟:用WCF实现对无人终端的远程监控

最近在项目中,遇到了一个需要在远程监视自动运行软件的实时情况的例子.因为MS面向服务方面有WCF,而且看了一些资料,觉得WCF比较适合这个应用.因此决定用WCF来实现这个功能. 首先,先说一下具体的应用,监控,顾名思义,有两个方面的意思,一方面是"监",也就是远程要能实时查看终端的各种情况.这里其实指的就是被监控的要能主动的,实时的向远程控制端发送自己的情况.另一方面是控,即远程端能够发布命令控制终端进行执行.并由终端返回一定的执行信息. 而且这里是一种一对一对多的关系,即一个终端可以

艾伟:[WCF中的Binding模型]之六(完结篇):从绑定元素认识系统预定义绑定

由于绑定对象由一系列有序的绑定元素组成,绑定元素最终决定着信道栈中信道的组成,而信道的组成最终又决定了信道栈对消息进行处理的方式和能力,所有要确定绑定的特性和能力,我们可以通过查看其绑定元素的构成来一窥究竟.为此我们我们写了一个简单的方法,用于列出一个具体的绑定对象所有的绑定元素,在介绍一个个具体的系统绑定中,我会使用该方法: static void ListAllBindingElements(Binding binding){ BindingElementCollection element

艾伟:WCF从理论到实践(14):WCF解决方案模板

本系列文章导航 WCF从理论到实践(1):揭开神秘面纱 WCF从理论到实践(2):决战紫禁之巅 WCF从理论到实践(3):八号当铺之黑色契约 WCF从理论到实践(4):路在何方 WCF从理论到实践(5):Binding细解 WCF从理论到实践(6):WCF架构 WCF从理论到实践(7):消息交换模式 WCF从理论到实践(8):事件广播 WCF从理论到实践(9):实例模式和对象生命周期 WCF从理论到实践(10):异常处理 WCF从理论到实践(11)-异步 WCF从理论到实践(12):事务 WCF

艾伟:WCF从理论到实践(6):WCF架构

本系列文章导航 WCF从理论到实践(1):揭开神秘面纱 WCF从理论到实践(2):决战紫禁之巅 WCF从理论到实践(3):八号当铺之黑色契约 WCF从理论到实践(4):路在何方 WCF从理论到实践(5):Binding细解 WCF从理论到实践(6):WCF架构 WCF从理论到实践(7):消息交换模式 WCF从理论到实践(8):事件广播 WCF从理论到实践(9):实例模式和对象生命周期 WCF从理论到实践(10):异常处理 WCF从理论到实践(11)-异步 WCF从理论到实践(12):事务 WCF

艾伟:WCF从理论到实践(11)-异步

本系列文章导航 WCF从理论到实践(1):揭开神秘面纱 WCF从理论到实践(2):决战紫禁之巅 WCF从理论到实践(3):八号当铺之黑色契约 WCF从理论到实践(4):路在何方 WCF从理论到实践(5):Binding细解 WCF从理论到实践(6):WCF架构 WCF从理论到实践(7):消息交换模式 WCF从理论到实践(8):事件广播 WCF从理论到实践(9):实例模式和对象生命周期 WCF从理论到实践(10):异常处理 WCF从理论到实践(11)-异步 WCF从理论到实践(12):事务 WCF

艾伟:WCF从理论到实践(7):消息交换模式

本系列文章导航 WCF从理论到实践(1):揭开神秘面纱 WCF从理论到实践(2):决战紫禁之巅 WCF从理论到实践(3):八号当铺之黑色契约 WCF从理论到实践(4):路在何方 WCF从理论到实践(5):Binding细解 WCF从理论到实践(6):WCF架构 WCF从理论到实践(7):消息交换模式 WCF从理论到实践(8):事件广播 WCF从理论到实践(9):实例模式和对象生命周期 WCF从理论到实践(10):异常处理 WCF从理论到实践(11)-异步 WCF从理论到实践(12):事务 WCF

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

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