如何:使用自定义用户名和密码验证程序

默认情况下,将用户名和密码用于身份验证时,Windows Communication Foundation (WCF) 会使用 Windows 来验证用户名和密码。 不过,WCF 允许自定义用户名和密码身份验证方案,也称为“验证程序”。 若要合并自定义用户名和密码验证程序,请创建一个从 UserNamePasswordValidator 派生的类,然后对其进行配置。

有关示例应用程序,请参阅用户名密码验证程序

创建自定义用户名和密码验证程序

  1. 创建一个从 UserNamePasswordValidator 派生的类。

    
    public class CustomUserNameValidator : UserNamePasswordValidator
    {
    
    Public Class CustomUserNameValidator
        Inherits UserNamePasswordValidator
    
  2. 通过重写 Validate 方法,实现自定义身份验证方案。

    请不要使用下面示例中的代码在生产环境中重写 Validate 方法。 请将该代码替换为你的自定义用户名和密码验证方案,这可能会涉及到从数据库检索用户名和密码对。

    若要将身份验证错误返回到客户端,应在 FaultException 方法中引发 Validate

    // This method validates users. It allows in two users, test1 and test2
    // with passwords 1tset and 2tset respectively.
    // This code is for illustration purposes only and
    // must not be used in a production environment because it is not secure.	
    public override void Validate(string userName, string password)
    {
        if (null == userName || null == password)
        {
            throw new ArgumentNullException();
        }
    
        if (!(userName == "test1" && password == "1tset") && !(userName == "test2" && password == "2tset"))
        {
            // This throws an informative fault to the client.
            throw new FaultException("Unknown Username or Incorrect Password");
            // When you do not want to throw an infomative fault to the client,
            // throw the following exception.
            // throw new SecurityTokenException("Unknown Username or Incorrect Password");
        }
    }
    
    ' This method validates users. It allows in two users, test1 and test2
    ' with passwords 1tset and 2tset respectively.
    ' This code is for illustration purposes only and
    ' must not be used in a production environment because it is not secure.
    Public Overrides Sub Validate(ByVal userName As String, ByVal password As String)
        If Nothing = userName OrElse Nothing = password Then
            Throw New ArgumentNullException()
        End If
    
        If Not (userName = "test1" AndAlso password = "[PLACEHOLDER]") AndAlso Not (userName = "test2" AndAlso password = "[PLACEHOLDER]") Then
            ' This throws an informative fault to the client.
            Throw New FaultException("Unknown Username or Incorrect Password")
            ' When you do not want to throw an informative fault to the client,
            ' throw the following exception:
            ' Throw New SecurityTokenException("Unknown Username or Incorrect Password")
        End If
    
    End Sub
    

配置服务以使用自定义用户名和密码验证程序

  1. 配置一个绑定,该绑定在任何传输上使用消息安全,或者在 HTTP(S) 上使用传输级安全。

    使用消息安全性时,请添加系统提供的绑定之一,例如 <wsHttpBinding>,或支持消息安全性和 UserName 凭据类型的 <customBinding>

    在通过 HTTP(S) 使用传输级安全性时,请添加 <wsHttpBinding><basicHttpBinding>,或者添加 <netTcpBinding> 或使用 HTTP(S) 和 Basic 身份验证方案的 <customBinding>

    备注

    使用 .NET Framework 3.5 或更高版本时,可将自定义用户名和密码验证程序与消息和传输安全性一起使用。 使用 WinFX 时,自定义用户名和密码验证程序只能与消息安全性一起使用。

    提示

    有关在此上下文中使用 <netTcpBinding> 的详细信息,请参阅 <security>

    1. 在配置文件中,在 <system.serviceModel> 元素下添加一个 <bindings> 元素。

    2. <wsHttpBinding><basicHttpBinding> 元素添加到 bindings 节。 有关创建 WCF 绑定元素的详细信息,请参阅如何:在配置中指定服务绑定

    3. <security>mode 属性或 <security> 设置为 MessageTransportTransportWithMessageCredential

    4. 设置 <message><transport>clientCredentialType 属性。

      使用消息安全性时,请将 <message>clientCredentialType 属性设置为 UserName

      通过 HTTP(S) 使用传输级安全性时,请将 <transport>clientCredentialType 属性或 <transport> 设置为 Basic

      备注

      如果使用传输级安全在 Internet Information Services (IIS) 中承载 WCF 服务并且 UserNamePasswordValidationMode 属性设置为 Custom,则自定义身份验证方案会使用 Windows 身份验证的子集。 这是因为,在此情况下,IIS 会在 WCF 调用自定义验证器之前执行 Windows 身份验证。

    有关创建 WCF 绑定元素的详细信息,请参阅如何:在配置中指定服务绑定

    以下示例演示了绑定的配置代码:

    <system.serviceModel>
      <bindings>
      <wsHttpBinding>
          <binding name="Binding1">
            <security mode="Message">
              <message clientCredentialType="UserName" />
            </security>
          </binding>
        </wsHttpBinding>
      </bindings>
    </system.serviceModel>
    
  2. 配置一个行为,该行为指定使用自定义用户名和密码验证程序来验证传入的 UserNameSecurityToken 安全令牌的用户名和密码对。

    1. 添加一个 <behaviors> 元素作为 <system.serviceModel> 元素的子级。

    2. <serviceBehaviors> 添加到 <behaviors> 元素。

    3. 添加一个 <behavior> 元素,并将 name 特性设置为适当的值。

    4. <serviceCredentials> 添加到 <behavior> 元素。

    5. <userNameAuthentication> 添加到 <serviceCredentials>

    6. userNamePasswordValidationMode 设置为 Custom

      重要

      如果未设置 userNamePasswordValidationMode 值,WCF 将使用 Windows 身份验证而不是自定义用户名和密码验证程序。

    7. customUserNamePasswordValidatorType 设置为表示自定义用户名和密码验证程序的类型。

    以下示例演示到目前为止的 <serviceCredentials> 片段:

    <serviceCredentials>
      <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Microsoft.ServiceModel.Samples.CalculatorService.CustomUserNameValidator, service" />
    </serviceCredentials>
    

示例

下面的代码示例演示如何创建自定义用户名和密码验证程序。 请不要使用代码重写生产环境中的 Validate 方法。 请将该代码替换为你的自定义用户名和密码验证方案,这可能会涉及到从数据库检索用户名和密码对。

using System;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;

using System.Security.Principal;

using System.ServiceModel;
Imports System.IdentityModel.Selectors
Imports System.IdentityModel.Tokens

Imports System.Security.Principal

Imports System.ServiceModel

public class CustomUserNameValidator : UserNamePasswordValidator
{
    // This method validates users. It allows in two users, test1 and test2
    // with passwords 1tset and 2tset respectively.
    // This code is for illustration purposes only and
    // must not be used in a production environment because it is not secure.	
    public override void Validate(string userName, string password)
    {
        if (null == userName || null == password)
        {
            throw new ArgumentNullException();
        }

        if (!(userName == "test1" && password == "1tset") && !(userName == "test2" && password == "2tset"))
        {
            // This throws an informative fault to the client.
            throw new FaultException("Unknown Username or Incorrect Password");
            // When you do not want to throw an infomative fault to the client,
            // throw the following exception.
            // throw new SecurityTokenException("Unknown Username or Incorrect Password");
        }
    }
}
Public Class CustomUserNameValidator
    Inherits UserNamePasswordValidator
    ' This method validates users. It allows in two users, test1 and test2
    ' with passwords 1tset and 2tset respectively.
    ' This code is for illustration purposes only and
    ' must not be used in a production environment because it is not secure.
    Public Overrides Sub Validate(ByVal userName As String, ByVal password As String)
        If Nothing = userName OrElse Nothing = password Then
            Throw New ArgumentNullException()
        End If

        If Not (userName = "test1" AndAlso password = "[PLACEHOLDER]") AndAlso Not (userName = "test2" AndAlso password = "[PLACEHOLDER]") Then
            ' This throws an informative fault to the client.
            Throw New FaultException("Unknown Username or Incorrect Password")
            ' When you do not want to throw an informative fault to the client,
            ' throw the following exception:
            ' Throw New SecurityTokenException("Unknown Username or Incorrect Password")
        End If

    End Sub
End Class

请参阅