Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este ejemplo se muestra cómo implementar una directiva de autorización de declaraciones personalizada y un administrador de autorización de servicio personalizado asociado. Esto resulta útil cuando el servicio realiza comprobaciones de acceso basadas en declaraciones en operaciones de servicio y, antes de las comprobaciones de acceso, concede al autor de la llamada ciertos derechos. En este ejemplo se muestra el proceso de agregar notificaciones, así como el proceso para realizar una comprobación de acceso en el conjunto finalizado de notificaciones. Todos los mensajes de aplicación entre el cliente y el servidor están firmados y cifrados. De forma predeterminada, con el wsHttpBinding
enlace, se usa un nombre de usuario y una contraseña proporcionados por el cliente para iniciar sesión en una cuenta de Windows válida. En este ejemplo se muestra cómo usar un personalizado UserNamePasswordValidator para autenticar al cliente. Además, en este ejemplo se muestra el cliente que se autentica en el servicio mediante un certificado X.509. En este ejemplo se muestra una implementación de IAuthorizationPolicy y ServiceAuthorizationManager, que entre ellas concede acceso a métodos específicos del servicio para usuarios específicos. Este ejemplo está basado en Nombre de usuario de seguridad de mensaje, pero muestra cómo realizar una transformación de la notificación antes de que se llame a ServiceAuthorizationManager.
Nota:
El procedimiento de instalación y las instrucciones de compilación de este ejemplo se encuentran al final de este tema.
En resumen, este ejemplo muestra cómo:
El cliente se puede autenticar mediante una contraseña de nombre de usuario.
El cliente se puede autenticar mediante un certificado X.509.
El servidor valida las credenciales de cliente en un validador personalizado
UsernamePassword
.El servidor se autentica mediante el certificado X.509 del servidor.
El servidor puede usar ServiceAuthorizationManager para controlar el acceso a determinados métodos del servicio.
Cómo implementar IAuthorizationPolicy.
El servicio expone dos puntos de conexión para comunicarse con el servicio, definidos mediante el archivo de configuración App.config. Cada punto de conexión consta de una dirección, un enlace y un contrato. Una vinculación está configurada con un enlace estándar wsHttpBinding
que utiliza WS-Security y la autenticación de nombre de usuario del cliente. El otro enlace se configura con un enlace estándar wsHttpBinding
que utiliza WS-Security y autenticación mediante certificado de cliente. El <comportamiento> especifica que se usarán las credenciales de usuario para la autenticación de servicio. El certificado de servidor debe contener el mismo valor para la SubjectName
propiedad que el findValue
atributo en <serviceCertificate>.
<system.serviceModel>
<services>
<service name="Microsoft.ServiceModel.Samples.CalculatorService"
behaviorConfiguration="CalculatorServiceBehavior">
<host>
<baseAddresses>
<!-- configure base address provided by host -->
<add baseAddress ="http://localhost:8001/servicemodelsamples/service"/>
</baseAddresses>
</host>
<!-- use base address provided by host, provide two endpoints -->
<endpoint address="username"
binding="wsHttpBinding"
bindingConfiguration="Binding1"
contract="Microsoft.ServiceModel.Samples.ICalculator" />
<endpoint address="certificate"
binding="wsHttpBinding"
bindingConfiguration="Binding2"
contract="Microsoft.ServiceModel.Samples.ICalculator" />
</service>
</services>
<bindings>
<wsHttpBinding>
<!-- Username binding -->
<binding name="Binding1">
<security mode="Message">
<message clientCredentialType="UserName" />
</security>
</binding>
<!-- X509 certificate binding -->
<binding name="Binding2">
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="CalculatorServiceBehavior" >
<serviceDebug includeExceptionDetailInFaults ="true" />
<serviceCredentials>
<!--
The serviceCredentials behavior allows one to specify a custom validator for username/password combinations.
-->
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Microsoft.ServiceModel.Samples.MyCustomUserNameValidator, service" />
<!--
The serviceCredentials behavior allows one to specify authentication constraints on client certificates.
-->
<clientCertificate>
<!--
Setting the certificateValidationMode to PeerOrChainTrust means that if the certificate
is in the user's Trusted People store, then it will be trusted without performing a
validation of the certificate's issuer chain. This setting is used here for convenience so that the
sample can be run without having to have certificates issued by a certification authority (CA).
This setting is less secure than the default, ChainTrust. The security implications of this
setting should be carefully considered before using PeerOrChainTrust in production code.
-->
<authentication certificateValidationMode="PeerOrChainTrust" />
</clientCertificate>
<!--
The serviceCredentials behavior allows one to define a service certificate.
A service certificate is used by a client to authenticate the service and provide message protection.
This configuration references the "localhost" certificate installed during the setup instructions.
-->
<serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
</serviceCredentials>
<serviceAuthorization serviceAuthorizationManagerType="Microsoft.ServiceModel.Samples.MyServiceAuthorizationManager, service">
<!--
The serviceAuthorization behavior allows one to specify custom authorization policies.
-->
<authorizationPolicies>
<add policyType="Microsoft.ServiceModel.Samples.CustomAuthorizationPolicy.MyAuthorizationPolicy, PolicyLibrary" />
</authorizationPolicies>
</serviceAuthorization>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Cada configuración del punto de conexión de cliente consta de un nombre de configuración, una dirección absoluta para el punto de conexión de servicio, el enlace y el contrato. El enlace del cliente se configura con el modo de seguridad adecuado tal y como se ha especificado en este caso en <security> y clientCredentialType
tal y como se ha definido en <message>.
<system.serviceModel>
<client>
<!-- Username based endpoint -->
<endpoint name="Username"
address="http://localhost:8001/servicemodelsamples/service/username"
binding="wsHttpBinding"
bindingConfiguration="Binding1"
behaviorConfiguration="ClientCertificateBehavior"
contract="Microsoft.ServiceModel.Samples.ICalculator" >
</endpoint>
<!-- X509 certificate based endpoint -->
<endpoint name="Certificate"
address="http://localhost:8001/servicemodelsamples/service/certificate"
binding="wsHttpBinding"
bindingConfiguration="Binding2"
behaviorConfiguration="ClientCertificateBehavior"
contract="Microsoft.ServiceModel.Samples.ICalculator">
</endpoint>
</client>
<bindings>
<wsHttpBinding>
<!-- Username binding -->
<binding name="Binding1">
<security mode="Message">
<message clientCredentialType="UserName" />
</security>
</binding>
<!-- X509 certificate binding -->
<binding name="Binding2">
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<behavior name="ClientCertificateBehavior">
<clientCredentials>
<serviceCertificate>
<!--
Setting the certificateValidationMode to PeerOrChainTrust
means that if the certificate
is in the user's Trusted People store, then it will be
trusted without performing a
validation of the certificate's issuer chain. This setting
is used here for convenience so that the
sample can be run without having to have certificates
issued by a certification authority (CA).
This setting is less secure than the default, ChainTrust.
The security implications of this
setting should be carefully considered before using
PeerOrChainTrust in production code.
-->
<authentication certificateValidationMode = "PeerOrChainTrust" />
</serviceCertificate>
</clientCredentials>
</behavior>
</behaviors>
</system.serviceModel>
Para el punto de conexión basado en nombres de usuario, la implementación del cliente establece el nombre de usuario y la contraseña que se van a usar.
// Create a client with Username endpoint configuration
CalculatorClient client1 = new CalculatorClient("Username");
client1.ClientCredentials.UserName.UserName = "test1";
client1.ClientCredentials.UserName.Password = "1tset";
try
{
// Call the Add service operation.
double value1 = 100.00D;
double value2 = 15.99D;
double result = client1.Add(value1, value2);
Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
...
}
catch (Exception e)
{
Console.WriteLine("Call failed : {0}", e.Message);
}
client1.Close();
Para el punto de conexión basado en certificados, la implementación del cliente establece el certificado de cliente que se va a usar.
// Create a client with Certificate endpoint configuration
CalculatorClient client2 = new CalculatorClient("Certificate");
client2.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "test1");
try
{
// Call the Add service operation.
double value1 = 100.00D;
double value2 = 15.99D;
double result = client2.Add(value1, value2);
Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
...
}
catch (Exception e)
{
Console.WriteLine("Call failed : {0}", e.Message);
}
client2.Close();
En este ejemplo se usa un personalizado UserNamePasswordValidator para validar los nombres de usuario y las contraseñas. El ejemplo implementa MyCustomUserNamePasswordValidator
, derivado de UserNamePasswordValidator. Consulte la documentación sobre UserNamePasswordValidator para obtener más información. Para mostrar la integración con UserNamePasswordValidator, este ejemplo de validador personalizado implementa el Validate método para aceptar pares de nombre de usuario y contraseña en los que el nombre de usuario coincide con la contraseña, como se muestra en el código siguiente.
public class MyCustomUserNamePasswordValidator : 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"))
{
throw new SecurityTokenException("Unknown Username or Password");
}
}
}
Una vez implementado el validador en el código de servicio, el host de servicio debe informarse sobre la instancia del validador que se va a usar. Esto se hace con el código siguiente:
Servicehost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
serviceHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new MyCustomUserNamePasswordValidatorProvider();
O bien, puede hacer lo mismo en la configuración:
<behavior>
<serviceCredentials>
<!--
The serviceCredentials behavior allows one to specify a custom validator for username/password combinations.
-->
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Microsoft.ServiceModel.Samples.MyCustomUserNameValidator, service" />
...
</serviceCredentials>
</behavior>
Windows Communication Foundation (WCF) proporciona un modelo robusto basado en declaraciones para realizar comprobaciones de acceso. El objeto ServiceAuthorizationManager se utiliza para realizar la comprobación de acceso y determinar si las declaraciones asociadas al cliente cumplen los requisitos necesarios para acceder al método del servicio.
Para fines de demostración, este ejemplo muestra una implementación de ServiceAuthorizationManager que implementa el método CheckAccessCore para permitir el acceso de un usuario a métodos basados en reclamaciones de tipo http://example.com/claims/allowedoperation
, cuyo valor es el URI de la acción de la operación a la que se permite llamar.
public class MyServiceAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
string action = operationContext.RequestContext.RequestMessage.Headers.Action;
Console.WriteLine("action: {0}", action);
foreach(ClaimSet cs in operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets)
{
if ( cs.Issuer == ClaimSet.System )
{
foreach (Claim c in cs.FindClaims("http://example.com/claims/allowedoperation", Rights.PossessProperty))
{
Console.WriteLine("resource: {0}", c.Resource.ToString());
if (action == c.Resource.ToString())
return true;
}
}
}
return false;
}
}
Una vez implementado el componente personalizado ServiceAuthorizationManager, se debe informar al host del servicio sobre el componente ServiceAuthorizationManager que se debe utilizar. Esto se hace como se muestra en el código siguiente.
<behavior>
...
<serviceAuthorization serviceAuthorizationManagerType="Microsoft.ServiceModel.Samples.MyServiceAuthorizationManager, service">
...
</serviceAuthorization>
</behavior>
El método IAuthorizationPolicy principal que implementar es el método Evaluate(EvaluationContext, Object).
public class MyAuthorizationPolicy : IAuthorizationPolicy
{
string id;
public MyAuthorizationPolicy()
{
id = Guid.NewGuid().ToString();
}
public bool Evaluate(EvaluationContext evaluationContext,
ref object state)
{
bool bRet = false;
CustomAuthState customstate = null;
if (state == null)
{
customstate = new CustomAuthState();
state = customstate;
}
else
customstate = (CustomAuthState)state;
Console.WriteLine("In Evaluate");
if (!customstate.ClaimsAdded)
{
IList<Claim> claims = new List<Claim>();
foreach (ClaimSet cs in evaluationContext.ClaimSets)
foreach (Claim c in cs.FindClaims(ClaimTypes.Name,
Rights.PossessProperty))
foreach (string s in
GetAllowedOpList(c.Resource.ToString()))
{
claims.Add(new
Claim("http://example.com/claims/allowedoperation",
s, Rights.PossessProperty));
Console.WriteLine("Claim added {0}", s);
}
evaluationContext.AddClaimSet(this,
new DefaultClaimSet(this.Issuer,claims));
customstate.ClaimsAdded = true;
bRet = true;
}
else
{
bRet = true;
}
return bRet;
}
...
}
El código anterior muestra cómo el Evaluate(EvaluationContext, Object) método comprueba que no se han agregado notificaciones nuevas que afecten al procesamiento y agregue notificaciones específicas. Las reclamaciones permitidas se obtienen del método GetAllowedOpList
, que está implementado para devolver una lista específica de operaciones que el usuario tiene permitido realizar. La política de autorización agrega reclamaciones para acceder a la operación determinada.
ServiceAuthorizationManager lo utiliza después para realizar decisiones de comprobación de acceso.
Una vez implementado el personalizado IAuthorizationPolicy , se debe informar al host de servicio sobre las directivas de autorización que se van a usar.
<serviceAuthorization>
<authorizationPolicies>
<add policyType='Microsoft.ServiceModel.Samples.CustomAuthorizationPolicy.MyAuthorizationPolicy, PolicyLibrary' />
</authorizationPolicies>
</serviceAuthorization>
Al ejecutar el ejemplo, las solicitudes de operación y las respuestas se muestran en la ventana de la consola del cliente. El cliente llama correctamente a los métodos Add, Subtract y Multiple y obtiene un mensaje "Access is denied" al intentar llamar al método Divide. Presione ENTRAR en la ventana del cliente para apagar el cliente.
Instalar el archivo por lotes
El archivo por lotes Setup.bat incluido con este ejemplo permite configurar el servidor con los certificados pertinentes para ejecutar una aplicación autohospedada que requiera seguridad basada en el certificado del servidor.
A continuación se proporciona una breve introducción a las distintas secciones de los archivos por lotes para que se puedan modificar para que se ejecuten en la configuración adecuada:
Creación del certificado de servidor.
Las líneas siguientes del archivo por lotes de Setup.bat crean el certificado de servidor que se va a usar. La variable %SERVER_NAME% especifica el nombre del servidor. Cambie esta variable para especificar su propio nombre de servidor. El valor predeterminado es localhost.
echo ************ echo Server cert setup starting echo %SERVER_NAME% echo ************ echo making server cert echo ************ makecert.exe -sr LocalMachine -ss MY -a sha1 -n CN=%SERVER_NAME% -sky exchange -pe
Instalar el certificado de servidor en el almacén de certificados de confianza del cliente.
Las líneas siguientes del archivo por lotes Setup.bat copian el certificado de servidor en el almacén de los usuarios de confianza del cliente. Este paso es necesario porque el sistema cliente no confía implícitamente en los certificados generados por Makecert.exe. Si ya tiene un certificado que se basa en un certificado raíz de confianza de cliente (por ejemplo, un certificado emitido por Microsoft), este paso para rellenar el almacén de certificados de cliente con el certificado de servidor no es necesario.
certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r CurrentUser -s TrustedPeople
Creación del certificado de cliente.
Las siguientes líneas del Setup.bat archivo por lotes crean el certificado de cliente que se va a usar. La variable %USER_NAME% especifica el nombre del servidor. Este valor se establece en "test1" porque este es el nombre que
IAuthorizationPolicy
busca. Si cambia el valor de %USER_NAME% debe cambiar el valor correspondiente en elIAuthorizationPolicy.Evaluate
método .El certificado está almacenado en Mi almacén (Personal) debajo de la ubicación de almacén CurrentUser.
echo ************ echo making client cert echo ************ makecert.exe -sr CurrentUser -ss MY -a sha1 -n CN=%CLIENT_NAME% -sky exchange -pe
Instalar el certificado de cliente en el almacén de certificados de confianza del servidor.
Las líneas siguientes del archivo por lotes Setup.bat copian el certificado del cliente en el almacén de los usuarios de confianza. Este paso es necesario porque el sistema del servidor no confía implícitamente en los certificados generados por Makecert.exe. Si ya tiene un certificado que se basa en un certificado raíz de confianza (por ejemplo, un certificado emitido por Microsoft), este paso para rellenar el almacén de certificados de servidor con el certificado de cliente no es necesario.
certmgr.exe -add -r CurrentUser -s My -c -n %CLIENT_NAME% -r LocalMachine -s TrustedPeople
Para instalar y compilar el ejemplo
Para compilar la solución, siga las instrucciones que se indican en Compilación de los ejemplos de Windows Communication Foundation.
Para ejecutar el ejemplo en una configuración de equipos única o cruzada, utilice las instrucciones siguientes.
Nota:
Si usa Svcutil.exe para volver a generar la configuración de este ejemplo, asegúrese de modificar el nombre del punto de conexión en la configuración de cliente para que coincida con el código de cliente.
Para ejecutar el ejemplo en el mismo equipo
Abra el símbolo del sistema para desarrolladores de Visual Studio con privilegios de administrador y ejecute Setup.bat desde la carpeta de instalación del ejemplo. Esto instala todos los certificados necesarios para ejecutar el ejemplo.
Nota:
El archivo por lotes Setup.bat está diseñado para ejecutarse desde el símbolo del sistema para desarrolladores de Visual Studio. La variable de entorno PATH que se establece en el símbolo del sistema para desarrolladores de Visual Studio señala al directorio que contiene los archivos ejecutables que requiere el script Setup.bat.
Inicie Service.exe desde service\bin.
Inicie Client.exe desde \client\bin. La actividad de cliente se muestra en la aplicación de consola cliente.
Si el cliente y el servicio no pueden comunicarse, consulte Sugerencias de solución de problemas para ejemplos de WCF.
Para ejecutar el ejemplo entre equipos
Cree un directorio en el equipo de servicio.
Copie los archivos de programa de servicio de \service\bin en el directorio del equipo de servicio. Copie también los archivos Setup.bat, Cleanup.bat, GetComputerName.vbs y ImportClientCert.bat en el equipo de servicio.
Cree un directorio en el equipo cliente para los archivos binarios del cliente.
Copie los archivos de programa cliente en el directorio cliente del equipo cliente. Copie también los archivos Setup.bat, Cleanup.baty ImportServiceCert.bat en el cliente.
En el servidor, ejecute
setup.bat service
en el símbolo del sistema para desarrolladores de Visual Studio abierto con privilegios de administrador.La ejecución
setup.bat
con elservice
argumento crea un certificado de servicio con el nombre de dominio completo del equipo y exporta el certificado de servicio a un archivo denominado Service.cer.Edite Service.exe.config para reflejar el nuevo nombre de certificado (en el
findValue
atributo de <serviceCertificate>), que es el mismo que el nombre de dominio completo del equipo. Cambie también el nombre del equipo en el <elemento service>/<baseAddresses> de localhost por el nombre completo del equipo de servicio.Copie el archivo Service.cer del directorio de servicio en el directorio cliente del equipo cliente.
En el servidor, ejecute
setup.bat client
en el símbolo del sistema para desarrolladores de Visual Studio abierto con privilegios de administrador.La ejecución
setup.bat
con elclient
argumento crea un certificado de cliente denominado test1 y exporta el certificado de cliente a un archivo denominado Client.cer.En el archivo Client.exe.config del equipo cliente, cambie el valor de dirección del punto de conexión para que coincida con la nueva dirección del servicio. Para ello, reemplace localhost por el nombre de dominio completo del servidor.
Copie el archivo Client.cer del directorio cliente en el directorio de servicio del servidor.
En el cliente, ejecute ImportServiceCert.bat en el símbolo del sistema para desarrolladores de Visual Studio abierto con privilegios de administrador.
Esto importa el certificado de servicio desde el archivo Service.cer al almacén CurrentUser - TrustedPeople.
En el servidor, ejecute ImportClientCert.bat en un símbolo del sistema para desarrolladores de Visual Studio abierto con privilegios de administrador.
Este importa el certificado de cliente del archivo Client.cer en el repositorio LocalMachine - TrustedPeople.
En el equipo servidor, inicie Service.exe desde la ventana de símbolo del sistema.
En el equipo cliente, inicie Client.exe desde una ventana de símbolo del sistema.
Si el cliente y el servicio no pueden comunicarse, consulte Sugerencias de solución de problemas para ejemplos de WCF.
Realización de una limpieza después de ejecutar el ejemplo
Para limpiar después del ejemplo, ejecute Cleanup.bat en la carpeta samples cuando haya terminado de ejecutar el ejemplo. Esto quita los certificados de servidor y cliente del almacén de certificados.
Nota:
Este script no quita certificados de servicio en un cliente al ejecutar este ejemplo entre equipos. Si ha ejecutado ejemplos de WCF que usan certificados entre equipos, asegúrese de borrar los certificados de servicio que se han instalado en el almacén CurrentUser - TrustedPeople. Para ello, use el siguiente comando: certmgr -del -r CurrentUser -s TrustedPeople -c -n <Fully Qualified Server Machine Name>
Por ejemplo: certmgr -del -r CurrentUser -s TrustedPeople -c -n server1.contoso.com
.