Procedimiento para usar un nombre de usuario personalizado y un validador de contraseñas

De forma predeterminada, cuando un nombre de usuario y una contraseña se utilizan para la autenticación, Windows Communication Foundation (WCF) usa Windows para validarlos. Sin embargo, WCF permite esquemas de autenticación de nombre de usuario y contraseña personalizados, también conocidos como validadores. Para incorporar un nombre de usuario personalizado y un validador de contraseña, cree una clase que derive de UserNamePasswordValidator y, a continuación, configúrela.

Para ver una aplicación de ejemplo, consulte Validador de contraseñas de nombre de usuario.

Para crear un nombre de usuario personalizado y un validador de contraseñas

  1. Cree una clase que derive de UserNamePasswordValidator.

    
    public class CustomUserNameValidator : UserNamePasswordValidator
    {
    
    Public Class CustomUserNameValidator
        Inherits UserNamePasswordValidator
    
  2. Implemente el esquema personalizado de autenticación invalidando el método Validate.

    No utilice el código en el ejemplo siguiente que invalida el método Validate en un entorno de producción. Reemplace el código con su nombre de usuario personalizado y esquema de validación de contraseña, que podrían implicar la recuperación de los pares de nombre de usuario y contraseña de una base de datos.

    Para devolver errores de autenticación al cliente, genere una FaultException en el método 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 informative 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
    

Para configurar un servicio con el fin de utilizar un nombre de usuario personalizado y un validador de contraseñas

  1. Configure un enlace que utilice la seguridad de mensaje sobre cualquier transporte o la seguridad del nivel de transporte sobre HTTP(S).

    Si usa la seguridad del mensaje, agregue uno de los enlaces proporcionados por el sistema, como <wsHttpBinding> o <customBinding> que admite la seguridad del mensaje y el tipo de credencial UserName.

    Al usar la seguridad de nivel de transporte a través de HTTP(S), agregue el elemento <wsHttpBinding> o <basicHttpBinding>, un elemento <netTcpBinding> o un elemento <customBinding> que use HTTP(S) y el esquema de autenticación Basic.

    Nota

    Si se usa .NET Framework 3.5 o una versión posterior, puede usar un validador de nombre de usuario y contraseña personalizado con la seguridad del mensaje y del transporte. Con WinFX, solo puede usarse un validador de nombre de usuario y contraseña con la seguridad del mensaje.

    Sugerencia

    Para más información sobre el uso de <netTcpBinding> en este contexto, consulte <security>.

    1. En el archivo de configuración, en el elemento < system.serviceModel>, agregue un elemento <bindings>.

    2. Agregue un elemento <wsHttpBinding> o <basicHttpBinding> a la sección de enlaces. Para más información sobre cómo crear un elemento de enlace de WCF, consulte Especificación de un enlace de servicio en la configuración.

    3. Establezca el atributo mode del elemento <security> o del elemento <security> en Message, Transport o TransportWithMessageCredential.

    4. Establezca el atributo clientCredentialType del elemento <message> o <transport>.

      Si usa la seguridad del mensaje, establezca el atributo clientCredentialType del elemento <message> en UserName.

      Si usa la seguridad de nivel de transporte a través de HTTP(S), establezca el atributo clientCredentialType del elemento <transport> o <transport> en Basic.

      Nota

      Cuando un servicio WCF se hospeda en Internet Information Services (IIS) mediante la seguridad de nivel de transporte, y se establece la propiedad UserNamePasswordValidationMode en Custom, el esquema de autenticación personalizado utiliza un subconjunto de autenticación de Windows. Esto se debe a que en este escenario, IIS realiza la autenticación de Windows antes de que WCF invoque al autenticador personalizado.

    Para más información sobre cómo crear un elemento de enlace de WCF, consulte Especificación de un enlace de servicio en la configuración.

    El siguiente ejemplo muestra el código de configuración del enlace:

    <system.serviceModel>
      <bindings>
      <wsHttpBinding>
          <binding name="Binding1">
            <security mode="Message">
              <message clientCredentialType="UserName" />
            </security>
          </binding>
        </wsHttpBinding>
      </bindings>
    </system.serviceModel>
    
  2. Configure un comportamiento que especifique que un nombre de usuario personalizado y el validador de contraseñas se utilizan para validar los pares de nombre de usuario y para los tokens de seguridad UserNameSecurityToken entrantes.

    1. Como elemento secundario del elemento <system.serviceModel>, agregue un elemento <behaviors>.

    2. Agregue un elemento <serviceBehaviors> al elemento <behaviors>.

    3. Agregue un elemento <behavior> y establezca el atributo name en un valor apropiado.

    4. Agregue un elemento <serviceCredentials> al elemento <behavior>.

    5. Agregue un elemento <userNameAuthentication> al elemento <serviceCredentials>.

    6. Establecer userNamePasswordValidationMode en Custom.

      Importante

      Si no se establece el valor userNamePasswordValidationMode, WCF usa la autenticación de Windows en lugar de validador de nombre de usuario y contraseña personalizados.

    7. Establezca customUserNamePasswordValidatorType en el tipo que representa su nombre de usuario personalizado y el validador de la contraseña.

    El ejemplo siguiente muestra el fragmento <serviceCredentials> en este punto:

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

Ejemplo

El ejemplo de código siguiente muestra cómo crear un nombre de usuario personalizado y el validador de la contraseña. No utilice el código que invalida el método Validate en un entorno de producción. Reemplace el código con su nombre de usuario personalizado y esquema de validación de contraseña, que podrían implicar la recuperación de los pares de nombre de usuario y contraseña de una base de datos.

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 informative 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

Consulte también