jueves, 31 de marzo de 2011

WCF: Windows Authentication and Impersonation

Voy a intentar mostrar todo el procedimiento de poder exponer un servicio WCF alojado en un IIS con autenticación windows, esto es una tarea bien sencilla, pero muchas veces si “googleamos” un poco, nos encontramos con diferentes entradas en los foros y cada uno intenta hacerlo de una forma, al final acabas pensando que es una tarea dificil cuando en realidad no lo es. Acto seguido expondré como poder realizar una impersonación con el usuario que ha llamado a nuestro servicio. 

Parte uno: creación WCF Service y publicación IIS

Lo primero que haremos será crear un proyecto de tipo WCF Service Application, en este proyecto tendremos nuestra interfaz que define el Service Contract con un método que define nuestro Operation Contract:

using System.ServiceModel;

namespace FooWcfService
{
[ServiceContract]
public interface IFooWcfService
{
[OperationContract]
string FooOperation();
}
}


Ahora modificaremos la clase que implementa nuestro Service Contract (en este tipo de proyectos, esta clase esta en el code behind del fichero .svc), en nuestro caso algo parecido a esto:



namespace FooWcfService
{
public class FooWcfService : IFooWcfService
{
public string FooOperation()
{
return "Hello World";
}
}
}


Muy bien, una vez tenemos implementado lo que hará nuestro servicio, hemos de empezar a tratar el tema de seguridad, en nuestro caso hemos dicho que tendremos autenticación windows. Para ello, tendremos que modificar el fichero web.config, y poner el siguiente binding:



<bindings>
<basicHttpBinding>
<binding>
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
</bindings>


Bueno, para que no haya dudas expondré todo el web.config:



<?xml version="1.0"?>
<configuration>

<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding>
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="FooWcfService.IFooWcfService">
<endpoint binding="basicHttpBinding" contract="FooWcfService.IFooWcfService" />
</service>
</services>
</system.serviceModel>

</configuration>


Ahora sí, ya estamos preparados para publicar nuestro servicio en un IIS. Supongamos que ya tenemos un IIS instalado, y que tenemos un Default Web Site, entonces sólo tendremos que hacer botón derecho sobre nuestro proyecto y publish.


publish web

Por último tendremos que habilitar en el IIS nuestra seguridad windows, vamos a nuestra web site y suponiendo que tengamos la vista Feature View, hacemos click sobre Authentication y habilitamos Windows Authentication y deshabilitamos Anonymous Authentication.


authentication iis

Parte dos: Impersonación



Para esta parte haremos un pequeño ejemplo de una aplicación cliente (Console application) que consuma nuestro servicio. Y cambiaremos un poco nuestra operación, para que tenga sentido la impersonación. Imaginemos que queremos tener un acceso a un recurso (por ejemplo acceso de escritura en disco) y para ello hemos de garantizar que el usuario que llama a nuestro servicio tenga permisos para ello, esto sería un claro ejemplo de impersonación. No se hable más, vamos a poner un pequeño ejemplo de ello:



using System;
using System.IO;

namespace FooWcfService
{
public class FooWcfService : IFooWcfService
{
public void FooOperation()
{
StreamWriter fileWriter = null;
try
{

fileWriter = File.CreateText(@"C:\dummy.txt");
fileWriter.Write("Hello world");

}
catch(Exception ex)
{
throw;
}
finally
{
if(fileWriter != null)
fileWriter.Close();
}
}
}
}


Para configurar la impersonación lo podemos hacer de modo imperativo o de modo declarativo, aquí podemos ver la explicación, nosotros en el ejemplo, usaremos el modo declarativo.



Este modo es el más simple, simplemente hemos de poner el siguiente atributo en la implementación de nuestro operation contract:

[OperationBehavior Impersonation=ImpersonationOption.Required)]

Si ejecutamos nuestra aplicación de consola, sin esta impersonación, lo más seguro es que nos aparezca un mensaje como el que apunto a continuación:

Access to the path 'C:\dummy.txt' is denied.

En cambio, si ponemos el attributo anterior, nos vuelve a dar una excepción pero ahora es diferente:

Either a required impersonation level was not provided, or the provided impersonation level is invalid.

Este mensaje nos esta diciendo, que nosotros requeriamos un cierto nivel de impersonación en nuestro código, pero que no se esta cumpliendo con la llamada desde el cliente.

Por lo tanto, el cliente cuando haga una llamada a nuestro servicio, ha de indicar que permite la impersonación.

Pongo ahora en código completo del cliente, done permitimos esta impersonación.

using System;
using System.Security.Principal;
using FooConsoleApplication.FooWcfService;

namespace FooConsoleApplication
{
class Program
{
static void Main(string[] args)
{
FooWcfServiceClient client = null;

try
{
client = new FooWcfServiceClient();
client.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
var result = client.FooOperation();

Console.Write(result);
Console.Read();
}
finally
{
if(client!= null)
{
client.Close();
}
}
}
}
}

4 comentarios:

Silvia dijo...

Hola,
Gracias x tu foro,necesito tu ayuda estoy intentando cambiar mi web.config x el tuyo pero me sale el siguiente error "Security settings for this service require Windows Authentication but it is not enabled for the IIS application that hosts this service." pero veo que IIS "Windows Authentication" esta checkeado, me podrias eyudar x fa

Jordi Ruiz dijo...

Hola Silvia,

¿está deshabilitada la auténticación anónima?

Pásame tu e-mail y lo comentamos.
Saludos,

Silvia dijo...

Hola eso es lo que dice pero esta habilitada si correo es silchamo@hotmail.com
Gracias

Anónimo dijo...

Hola,
Como es la forma de autenticar ciertos clientes, enviando el usuario y contraseña encriptadas?