WCF安全之customBinding

本文是关于WCF安全的一个完整示例,WCF宿主选用IIS,并通过添加“启用Silverlight功能的WCF服务”的方式建立WCF服务。

WCF服务的绑定方式采用Visual Studio 2008默认的customBinding,安全模式选用Transport安全模式(httpsTransport),身份验证模式则选用UserNameOverTransport。

本示例的客户端为Silverlight,并在访问WCF服务时使用了Visual Studio 2008自动生成的代理类。

测试用服务类:

using System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Collections.Generic;
using System.Text;

namespace SecSample.Web
{
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class WeatherService
    {
        [OperationContract]
        public string GetWeather()
        {
            return "Sunny";
        }
    }
}

1、配置IIS

1.1 生成证书

可以使用Makecert.exe工具生成一个测试用的证书,命令如下:

makecert -sr localmachine -ss My -n CN=localhost -sky exchange -pe -r

 

其中cn=localhost,表示证书的名称为localhost。

本环节需要注意的问题是:

a. 证书名称必须与访问WCF服务所用的域名或机器名一致,否则用IE访问时会出现如下警告

image

用WCF客户端访问时则会导致“无法为 SSL/TLS 安全通道与颁发机构“XXXX”建立信任关系。”的错误。

该限制的直接后果就是造成无法通过不同域名或IP地址访问同一个网站(比如用localhost或192.168.1.8访问本机),暂未找到生成多域名证书的方法,正在郁闷中,如有高手知道解决措施,望不吝赐教。

b. 以上命令行生成的证书存储在“个人”证书中,需要导入到“受信任的根证书颁发机构”或“受信任的发布者”中。

1.2 为IIS配置绑定

编辑网站绑定,添加https类型,并指定SSL证书。如下图:

image

2、服务端配置

Web.config文件中system.serviceModel节的代码如下:

  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="defaultServiceBehavior">
          <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
          <serviceCredentials>
            <userNameAuthentication userNamePasswordValidationMode="Custom"
              customUserNamePasswordValidatorType="SecSample.Web.CustomUserNameValidator,SecSample.Web" />
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <customBinding>
        <binding name="defaultCustomBinding">
          <binaryMessageEncoding />
          <security authenticationMode="UserNameOverTransport"/>
          <httpsTransport/>
        </binding>
      </customBinding>
    </bindings>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
    <services>
      <service behaviorConfiguration="defaultServiceBehavior" name="SecSample.Web.WeatherService">
        <endpoint address="" binding="customBinding" bindingConfiguration="defaultCustomBinding"
          contract="SecSample.Web.WeatherService" />
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>
    </services>
  </system.serviceModel>

serviceBehaviors节中命名为defaultServiceBehavior的behavior设置了如下内容:

a. 允许https协议获取元数据

b. userNamePasswordValidationMode选用Custom,验证类为继承自UserNamePasswordValidator类的CustomUserNameValidator类,CustomUserNameValidator类的完整源代码如下:

using System;
using System.Collections.Generic;
using System.IdentityModel.Selectors;
using System.Linq;
using System.ServiceModel;
using System.Web;

namespace SecSample.Web
{
    public class CustomUserNameValidator : UserNamePasswordValidator
    {

        public override void Validate(string userName, string password)
        {
            if (userName == "admin" && password == "123456") return;
            throw new ApplicationException("验证失败!");
        }
    }
}

 

 

customBinding节中命名为defaultCustomBinding的binding设置了customBinding所选用的编码方式(binaryMessageEncoding)、安全验证模式(UserNameOverTransport)及传输协议(httpsTransport)。

3、客户端配置

客户端ServiceReferences.ClientConfig文件的完整代码如下:

<configuration>
  <system.serviceModel>
    <bindings>
      <customBinding>
        <binding name="defaultCustomBinding">
          <binaryMessageEncoding />
          <security authenticationMode="UserNameOverTransport" />
          <httpsTransport />
        </binding>
      </customBinding>
    </bindings>
    <client>
      <endpoint address="../WeatherService.svc" binding="customBinding"
        bindingConfiguration="defaultCustomBinding" contract="WeatherServiceRef.WeatherService"
        name="CustomBinding_WeatherService" />
    </client>
  </system.serviceModel>
</configuration>

由于Silverlight及WCF使用同一个网站承载并假定相对位置固定,客户端终结点使用了相对地址“../WeatherService.svc”

以避免更换域名造成的问题(可惜证书与域名绑定的问题避免不了),customBinding节中则作了与服务器端对应的配置。

4、客户端示例页面

MainPage.xaml文件:

<UserControl x:Class="SecSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
  <Grid x:Name="LayoutRoot">
        <Button Name="btnGet" Content="GetWeather" Click="GetWeather"
                HorizontalAlignment="Center" VerticalAlignment="Center"></Button>
  </Grid>
</UserControl>

MainPage.xaml.cs文件:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using SecSample.WeatherServiceRef;

namespace SecSample
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();

            _client = new SecSample.WeatherServiceRef.WeatherServiceClient();

            _client.ClientCredentials.UserName.UserName = "admin";
            _client.ClientCredentials.UserName.Password = "123456";

             
            _client.GetWeatherCompleted += new EventHandler<GetWeatherCompletedEventArgs>(_client_GetWeatherCompleted);
        }
        
        private WeatherServiceClient _client;

        private void GetWeather(object sender, RoutedEventArgs e)
        {
            this._client.GetWeatherAsync();
            this.btnGet.IsEnabled = false;
        }

        void _client_GetWeatherCompleted(object sender, GetWeatherCompletedEventArgs e)
        {
            this.btnGet.IsEnabled = true;
            
            if (e.Error == null)
            {
                MessageBox.Show(e.Result);
            }
            else
            {
                MessageBox.Show(e.Error.Message);
            }
        }
    }
}

示例页面非常简单,添加一个按钮并在按钮单击时调用WCF服务,调用成功时显示调用结果,否则显示错误信息。

需注意的是客户端代理类的初始化,在服务器端要求UserNameOverTransport验证后,需要为客户端代理类指定UserName及Password。

至此,整个示例就完成了。运行效果图如下:

image

但如果用另一个机器访问该服务,会可能会出现如下警告:

image

这是由于我们使用的只是自己生成的测试用证书,而该证书并没有受到客户机的信任。如果只是测试或企业内部使用,客户端将该证书安装到受信任区域即可,如果是正式对外提供服务,则可能需要向相关机构申请正式证书。

示例测试环境:

操作系统:Windows7

开发环境:Visual Studio 2008 + Silverlight 3

IIS:7.5

浏览器:IE8

posted @ 2010-04-28 18:48  同一片海  阅读(3119)  评论(2编辑  收藏  举报