2009-07-20 7 views
9

Estoy tratando de extender WCF para que pueda tener un servicio web RESTful, en el cual, para cada operación, realizo una verificación del encabezado de Autorización HTTP, cuyo valor utilizo para llamar a un método de Login().¿Cuándo y dónde establecer un IOperationInvoker personalizado?

Después de finalizar el inicio de sesión, deseo invocar el método correspondiente para verificar si se lanza una excepción de seguridad, en cuyo caso responderé con un mensaje personalizado de "acceso denegado" utilizando el código de estado HTTP apropiado.

con esto en mente, pensé que la implementación de un IEndpointBehavior que aplica una implementaion de IOperationInvoker a cada operación (estableciendo la propiedad DispatchOperation.Invoker) sería una buena idea.

decidí poner en práctica un IOperationInvoker usando el diseño del decorador Mi implementación necesitaría otro IOperationInvoker en su constructor al que se delegarían las invocaciones de métodos .

Esta es mi IOperationInvokerImplementation:

public class BookSmarTkOperationInvoker : IOperationInvoker{ 

    private readonly IOperationInvoker invoker; 

    public BookSmarTkOperationInvoker(IOperationInvoker decoratee) 
    { 
     this.invoker = decoratee; 
    } 

    public object[] AllocateInputs() 
    { 
     return this.invoker.AllocateInputs(); 
    } 

    public object Invoke(object instance, object[] inputs, out object[] outputs) 
    { 
     BeforeOperation(); // Where there's code to perform the login using WebOperationContext.Current 
     object o = null; 
     try 
     { 
      o = this.invoker.Invoke(instance, inputs, out outputs); 
     } 
     catch (Exception exception) 
     { 
      outputs = null; 
      return AfterFailedOperation(exception); // Return a custom access denied response 
     } 

     return o; 
    } 

    public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state) 
    { 
     throw new Exception("The operation invoker is not asynchronous."); 
    } 

    public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) 
    { 
     throw new Exception("The operation invoker is not asynchronous."); 
    } 

    public bool IsSynchronous 
    { 
     get 
     { 
      return false; 
     } 
    } 
} 

decidí poner en práctica un IEndpointBehavior extendiendo el comportamiento que ya necesitaba (WebHttpBehavior) de esta manera Yo sólo uso uno beavior. Aquí está el código que escribí:

public class BookSmarTkEndpointBehavior : WebHttpBehavior 
{ 
    public override void Validate(ServiceEndpoint endpoint) 
    { 
     base.Validate(endpoint); 
    } 

    public override void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) 
    { 
     base.AddBindingParameters(endpoint, bindingParameters); 
    } 

    public override void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) 
    { 
     base.ApplyDispatchBehavior(endpoint, endpointDispatcher); 

     foreach (DispatchOperation operation in endpointDispatcher.DispatchRuntime.Operations) 
     { 
      IOperationInvoker defaultInvoker = operation.Invoker; 
      IOperationInvoker decoratorInvoker = new BookSmarTkOperationInvoker(defaultInvoker); 
      operation.Invoker = decoratorInvoker; 

      Console.Write("Before: " + ((object)defaultInvoker ?? "null")); 
      Console.WriteLine(" After: " + operation.Invoker); 
     } 
    } 

    public override void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) 
    { 
     base.ApplyClientBehavior(endpoint, clientRuntime); 
     throw new Exception("The BookSmarTkEndointBehavior cannot be used in client endpoints."); 
    } 
} 

Ahora aquí está el problema:

  1. Sólo el constructor se invoca en el IOperationInvoker, ninguno de los otros métodos son.
  2. El IOperationInvoker decorado (el que se pasa en el constructor del decorador) es nulo.

supongo que tal vez algún otro código de algún otro comportamiento está poniendo otra IOperationInvoker en el OperationDispatcher.Invoker poniendo a continuación. Por lo tanto, anulando el mío. Esto explicaría claramente mi situación.

¿Qué está pasando y qué debo hacer?

Mi servicio es de alojamiento.

En caso de que necesite verlo, aquí está la configuración que tengo en el archivo app.config en system.serviceModel.

<services> 
    <service name="BookSmarTk.Web.Service.BookSmarTkService"> 
    <host> 
     <baseAddresses> 
     <add baseAddress="http://localhost:8080/service"/> 
     </baseAddresses> 
    </host> 
    <endpoint 
     address="" 
     behaviorConfiguration="BookSmaTkEndpointBehavior" 
     binding="webHttpBinding" 
     bindingConfiguration="BookSmarTkBinding" 
     contract="BookSmarTk.Web.Service.BookSmarTkService"> 
    </endpoint> 
    </service> 
</services> 

<behaviors> 
    <serviceBehaviors> 
    <behavior name ="BookSmartkServiceBehavior"> 
     <serviceDebug httpHelpPageEnabled="true" httpHelpPageUrl="/help.htm" includeExceptionDetailInFaults="true" /> 
    </behavior> 
    </serviceBehaviors> 
    <endpointBehaviors> 
    <behavior name="BookSmaTkEndpointBehavior"> 
     <!--<webHttp/>--> 
     <bookSmarTkEndpointBehavior /> 
    </behavior> 
    </endpointBehaviors> 
</behaviors> 

<bindings> 
    <webHttpBinding> 
    <binding name="BookSmarTkBinding"> 
    </binding> 
    </webHttpBinding> 
</bindings> 

<extensions> 
    <behaviorExtensions> 
    <add name="bookSmarTkEndpointBehavior" type="BookSmarTk.Web.Service.BookSmarTkEndpointBehaviorElement, BookSmarTk.Web.Service, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/> 
    </behaviorExtensions> 
</extensions> 

Léelo hasta aquí Estoy profundamente agradecido con usted. ¡De verdad gracias!

Respuesta

2

Estoy construyendo algo similar (creo que no tengo tiempo para revisar todo el código), pero lo he hecho de otra manera.

Para lograr esto estoy usando lo siguiente:

  • Un IMessageInspector para leer los encabezados de entrada de petición HTTP mensaje (en este caso la extracción de una sesión de identificación de una galleta y recuperar un objeto de sesión desde una memoria caché).
  • Combinación de IPrincipal y IAuthorizationPolicy para implementar mi propio código de autorización personalizado (WCF invocará automáticamente mi código para solicitudes a métodos de servicio web que tengan el atributo '[PrincipalPermission (SecurityAction.Demand, Role = "somerole")] 'establecer).
  • IErrorHandler que detecta excepciones no detectadas de los métodos del servicio web (incluida una excepción de permiso denegado lanzado si falla la autorización, es decir, el método IsRole que implementa en IPrincipal devuelve falso). Si detecta la excepción de seguridad denegada, puede usar WebOperationContext.Current para establecer los códigos de error HTTP personalizados para el mensaje de respuesta.
  • Comportamiento personalizado (un comportamiento IContract, pero también puede usar un EndPoint o comportamiento de servicio o lo que usted desee) que crea todo lo anterior en tiempo de ejecución y los conecta con los puntos finales apropiados.
12

En lugar de establecer invocadores al método ApplyDispatchBehavior(), usted tiene que hacer un implementador IOperationBehavior:

public class MyOperationBehavior: IOperationBehavior 
{ 
    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) 
    { 
    } 

    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) 
    { 
    } 

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) 
    { 
    dispatchOperation.Invoker = new BookSmarTkOperationInvoker(dispatchOperation.Invoker); 
    } 

    public void Validate(OperationDescription operationDescription) 
    { 
    } 
} 

y luego a ApplyDispatchBehavior() debe establecer que el comportamiento:

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) 
    { 
    foreach (var operation in endpoint.Contract.Operations) { 
     if (operation.Behaviors.Contains(typeof(MyOperationBehavior))) 
     continue; 

     operation.Behaviors.Add(new MyOperationBehavior()); 
    } 
    } 
+2

Este solución trabajó conmigo +1 –

Cuestiones relacionadas