65.9K
CodeProject 正在变化。 阅读更多。
Home

带有自定义用户名密码身份验证的 WCF 服务

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (61投票s)

2010年7月23日

CPOL

5分钟阅读

viewsIcon

469617

downloadIcon

16020

本文介绍了一种自定义用户名密码身份验证的方法,无需在客户端安装证书。

使用的工具

  1. Pluralsight Self-Cert 工具。此工具由 Pluralsight 提供,用于创建和安装证书。
  2. WinHttpCertCfg.exe。Windows HTTP Services Certificate Configuration Tool 是一个命令行工具,用于授予特定用户对证书私钥文件的读取权限。
  3. 环境:Visual Studio 2010,以及 IIS7 或更高版本。

目录

  1. 引言
  2. 创建服务
  3. 配置服务
  4. 配置 IIS 并发布网站
  5. 安装服务器端证书
  6. 设置客户端

引言

Windows Communication Foundation 提供了丰富的安全功能,例如传输级别、消息级别以及传输与消息结合的安全模式;每种安全类型都有其优点和开销。我的应用程序有许多不同的客户端用于连接服务,并且它们必须经过数据库身份验证,因此最佳解决方案是使用自定义用户名-密码身份验证的消息级别安全。在深入研究网络后,我找到了信息片段,并经过一番努力,实现了一个具体的解决方案,希望它对其他人有所帮助。

创建服务

该解决方案使用 VS2010 创建,包含三个项目:WCF 服务、网站和桌面应用程序(客户端应用程序)。

solution.jpg

WCF 服务仅包含一个函数 GetServertime()

[ServiceContract]
public interface IService1
{
    [OperationContract]
    string GetServertime();
}

[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class Service1 : IService1
{  
    public string GetServertime()
    {
        return DateTime.Now.ToString();
    }   
}

我们创建一个类并命名为 UserNamePassValidator。然后我们将此代码实现到其中。

using System;
using System.ServiceModel;

namespace CustomUsernamePasswordAuth.Service
{
    class UserNamePassValidator : 
          System.IdentityModel.Selectors.UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            if(  userName==null ||  password==null)
            {
                throw new ArgumentNullException();
            }

            if (!(userName == "fayaz" && password == "soomro") )
            {
                throw new FaultException("Incorrect Username or Password");
            }
        }
    }
}

此类必须派生自 System.IdentityModel.Selectors.UserNamePasswordValidator 并重写 Validate 方法。要验证用户,请使用任何数据源;在本例中,我们将使用硬编码值。

创建 Web 应用程序

在 Web 应用程序中添加对服务的引用。添加一个文本文件并将其重命名为 UserNamePassService.svc,并添加以下代码行

<%@ ServiceHost Language="C#" Debug="true" 
    Service="CustomUsernamePasswordAuth.Service.Service1" %>

配置 Web 服务

修改 web.config 文件并添加以下行。

添加一个名为 Behavior1 的服务行为。通过添加 <serviceMetadata httpGetEnabled="true"/> 来启用服务元数据,这样当我们在客户端应用程序中添加服务引用时,它就能获取信息并为我们创建代理类。而关键部分是服务证书。证书创建将在后续部分介绍,但现在,我们需要记住证书设置。 FindValye="MyWebSite" 将是证书 CN=MyWebSite 的主题,您可以将此值更改为您的域名或项目名称。

usernamepasswordvalidation 模式设置为 custom,并且 customUsernameapsswordValidator 需要指定自定义验证类和命名空间。

<system.serviceModel>        
    <behaviors>
        <serviceBehaviors>
            <behavior name="Behavior1">
                <serviceMetadata httpGetEnabled="true" />
                <serviceDebug includeExceptionDetailInFaults="true" />
                <serviceCredentials> 
                    <serviceCertificate findValue="MyWebSite" 
                          storeLocation="LocalMachine"
                          storeName="My" 
                          x509FindType="FindBySubjectName" />
                    <userNameAuthentication userNamePasswordValidationMode="Custom"
                     customUserNamePasswordValidatorType="CustomUsernamePasswordAuth.
                        Service.UserNamePassValidator, CustomUsernamePasswordAuth.Service" />
                </serviceCredentials>
            </behavior>             
        </serviceBehaviors>
    </behaviors>

如下所示设置绑定配置。将其命名为 Binding1,并将安全模式设置为 MessageclientCredentialType 设置为 "username"。

<bindings>
    <wsHttpBinding>
        <binding name="Binding1">
            <security mode="Message">
                <message clientCredentialType="UserName"/>
            </security>
        </binding>
    </wsHttpBinding>
</bindings>

现在我们将设置服务终结点。有两个终结点:wsHttp 终结点和用于元数据交换的 Mex 终结点。基地址是 https:///。完全限定的服务地址将是 https:///UserNamePassService.svc

<services>
    <service behaviorConfiguration="Behavior1" 
              name="CustomUsernamePasswordAuth.Service.Service1">
    <endpoint address="" binding="wsHttpBinding"    
              bindingConfiguration="Binding1"
              contract="CustomUsernamePasswordAuth.Service.IService1" />
     <endpoint address="mex" binding="mexHttpBinding" 
        contract="IMetadataExchange" />
            <host>
                <baseAddresses>
                    <add baseAddress="https:///" />
                </baseAddresses>
            </host>
        </service>
    </services>         
</system.serviceModel>

注意:如果将在 IIS 中的特定端口上托管网站,如本例所示,我们将网站托管在 IIS 的 83 端口上,https://:83/UserNamePassService.svc,则无需更改配置文件中的端口,并将 baseAddress 保持为 "https:///"。

在 IIS 7 中创建站点

打开 IIS Manager。右键单击 Sites 并选择 Add Website。将其命名为 WebSite,将 Application pool 设置为 DeafaultAppPool,并选择物理路径,将端口设置为 83。如下图所示

iis.jpg

将 DefaultAppPool Framework 版本设置为 4.0。

iis2.jpg

将网站发布到 IIS

在 Solution Explorer 中右键单击网站项目并将其发布。选择 Publish method 为 File system,Target location 为 https://:83,如下图所示

publsih.jpg

浏览网站。打开 Internet Explorer 并输入 https://:83/UserNamePassService.svc。您将看到 X.509 找不到的错误。

browse1.jpg

安装证书

从文章开头提供的链接下载 Pluralsight SelfCert。以管理员身份运行该工具;否则,它将崩溃。

配置设置以安装证书;请参阅下面的屏幕截图。

certificate.jpg

进行所需更改后,单击 Save 按钮,然后您将看到下面的屏幕

Cert1.jpg

安装证书后,再次浏览网站,但这次您应该会看到一个不同的错误,如下图所示

browse2.jpg

此错误表示默认应用程序池没有证书私钥的访问权限,因此现在我们需要授予默认应用程序池读取访问权限。

从文章开头提供的链接下载 WinHttpCertCfg.exe。这是一个命令行工具。安装该工具后,以管理员身份在命令提示符下运行以下命令。

C:\Program Files (x86)\Windows Resource Kits\Tools>winhttpcertcfg 
             -g -c LOCAL_MACHINE\My -s MyWebSite -a DefaultAppPool

运行命令后,您将看到如下屏幕

commandline.jpg

现在再次浏览网站。并验证服务,它应该已启动。

browse3.jpg

最后一步是创建一个客户端来消耗服务

客户端应用程序是桌面应用程序,仅包含地址文本框和获取服务器时间的按钮。

client1.jpg

现在向项目中添加服务引用

client2.jpg

Button_click 事件添加代码

private void button1_Click(object sender, EventArgs e)
{
    string time = "";
    // Method 1: Create the client using the configuration file

    Service1Client c = new Service1Client();
    c.ClientCredentials.UserName.UserName = "fayaz";
    c.ClientCredentials.UserName.Password = "soomro";
    c.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = 
                        X509CertificateValidationMode.None;
    time = c.GetServertime();
    MessageBox.Show(time);

    // Method 2: Creating the client by creating endpoint and binding through coding
    var ServiceendPoint = new EndpointAddress(new Uri(txtServiceAddress.Text), 
                          EndpointIdentity.CreateDnsIdentity("MyWebSite"));
    var binding = new WSHttpBinding();
    binding.Security.Mode = SecurityMode.Message;
    binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;

    var result = new Service1Client(binding, ServiceendPoint);
    result.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = 
                             X509CertificateValidationMode.None;            
    result.ClientCredentials.UserName.UserName = "fayaz";            
    result.ClientCredentials.UserName.Password = "soomro";
    time = result.GetServertime();
    MessageBox.Show(time);
}

运行应用程序

client3.jpg

从另一台 PC 运行客户端以确保一切正常

client4.jpg

结论

我相信这个项目对于想要实现自定义安全性的开发人员来说会很有用。我已尽我最大的努力用屏幕截图描述了每一步。希望您喜欢这篇文章。如果您喜欢这篇文章,请告诉我 :)。如果您有任何问题,请随时通过 fayaziiui@gmail.com 与我联系。

© . All rights reserved.