Access HttpContext from Silverlight




1º Introducción Silverlight SandBox

Silverlight es un plugin diseñado para ser “ultra” seguro,
tanto, que desde una aplicación silverlight no podemos acceder a priori, a
Dll’s que no estén autorizadas para usar desde los proyectos de tipo
Silverlight de visualStudio, por ejemplo, no podemos acceder a una capa de BLL
o a una DAL sencillita, si esta no está dentro del proyecto Silverlight, y
todos los accesos que queramos hacer a través de estos, tendrán que ser vía
Servicios, POST, REST etc..

Silverlight además, se ejecuta de forma aislada, es decir,
que no sabe dónde se está ejecutando, ni quien lo hace, ya que es un plugin que
puede ejecutarse tanto desde un explorador web como desde el propio escritorio
de Windows.

2º Problemática en el acceso a datos

Imaginemos una simple consulta en la que a partir del
usuario con el que estoy logado, quiero ver mis facturas de la luz (antes de
ser estoicamente violadas por el nuevo subidón de casi un 10% en tiempos de
crisis xd). Cómo le mandaría a la select en cuestión el usuario que está
ejecutando la aplicación por ejemplo el HttpContext.Current.User.Identity.Name , si desde el control silverlight no sé quién está
ejecutando la aplicación, ni siquiera dónde se está ejecutando el mismo ¿?,
cómo veis surge un problemilla…

Bien, la respuesta es simple. Es imposible, por lo menos de una manera elegante y segura, digo
esto ya que podría “ñapearse” el canal de comunicación exitente entre el DOM
reenderizado de la página que lo contiene y la ejecución del mismo control, a
partir de los initParams del Object que embeberá el ActiveX, pudiendo así
hacer una llamada “customizada” al servicio WCF que lanzará la select, pero el
problema es que se descubrirían los datos de la comunicación en el DOM de la
página.

Dicho esto, empecé a mirar alternativas llegando a la
conclusión de que lo mejor, no era hacer que el control Silverlight supiera quién
o cómo se está ejecutando, usemos el control
como lo que es, un control que no tiene que saber ningún dato de negocio,
tratémosle como si fuera una capa de presentación tonta e independiente.

Pero ojo, si estamos usando esa especie de careta para
obtener datos de negocio, cómo conseguiremos llegar a nuestra capa de datos con
lo necesario ¿?, pues simple, haciendo que sea la propia capa de acceso a datos,
la que sepa cómo se está ejecutando, es decir. El control silverlight accederá
a los datos a través de un servicio WCF y será
este el que sea capaz de saber dónde,  cómo y quién lo está ejecutando
, a través
del contexto en el que stá ejecutandose, para ello el servicio se publicará dentro del mismo site que usa los distintos
controles silverlight
, de tal manera, que la publicación atendería a la
ilustración 1:

Ilustración 1

3º Servicios publicados dentro del Site:

Esta es una de las múltiples maneras que existen para
securizar un servicio web. Si este únicamente
va a ser accesible desde una aplicación
puede publicarse en el mismo
entorno de ejecución de la misma. De tal forma que heredaría la seguridad implícita
de la aplicación y tendría acceso al contexto.

De esta manera, mataríamos dos pájaros de un tiro, ya que
siempre que alguien llame a ese servicio, si antes no se ha validado en la
aplicación en cuestión, no podrá invocarlo, y por otro lado, cuando nuestro
control silverlight lo invoque, este podrá obtener los datos que dependen del usuario que lo ejecuta.

La idea podría representarse esquemáticamente de la
siguiente manera:

Ilustración 2

Hay que destacar que para que esta arquitectura funcione,
los servicios deben tener una configuración un tanto especial, para que puedan
“saber leer su entorno”

Para ello bastará con:

          a)       Habilitar el atributo AspNetCompatibilityRequirements.

[AspNetCompatibilityRequirements(RequirementsMode
= AspNetCompatibilityRequirementsMode.Allowed)]

public class
Servicio1: iServicio1

{

public string
GetHttpContextUser()

{

if (!System.Web.HttpContext.Current.User.Identity.IsAuthenticated)

throw new Exception();

                               return
System.Web.HttpContext.Current.User.Identity.Name;

                }

}

 

                   b)       Agregar el elemento <serviceHostingEnvironment>
a la configuración del servicio, en la sección <system.serviceModel>,
para habilitar la compatibilidad con ASP.NET.


<serviceHostingEnvironment aspNetCompatibilityEnabled=true />

 

 

 4. Arquitectura lógica de publicación:

Atendiendo a esta idea, podríamos tener varias opciones a la
hora de montar una arquitectura optima de petición / respuesta, de las que
destacaré dos:

4.1 Acceso
Directo:

He denominado Acceso
Directo
a la opción que podemos apreciar en la Ilustración 3.

 

Ilustración 3

Como podemos ver, los distintos servicios WCF se ejecutan
dentro del entorno de la aplicación del Portal,y el control
Silverlight accede directamente a los mismos.

Como se comentaba anteriormente, al estar ejecutándose
dentro del entorno de la aplicación, (a no ser que se le especifique lo
contrario), cualquier petición que se haga a los mismos, debería estar
autenticada a trabes del Membership de seguridad del portal.

En estos servicios web, estará la lógica de negocio
necesaria para aislar la Interfaz de usuario en Silverlight de la capa de
datos. Con esta solución resolveríamos los dos problemas que se nos plantean:

       
Servicios que conocen el contexto desde el que
se les está llamando y Servicios securizados a los que no se les puede
llamar si no estas autenticado en la aplicación.

Pero hay algo importante a destacar, y es que esta solución
plantea un problema. Y es que en los servidores con IIS6 y un Framework
inferior al 4.0, existe un BUG, que
consiste en que cuando un IIS aloja una aplicación web y esta hace una petición
a un servicio que cuelga de la misma aplicación, recibe varias BaseAddress y no
es capaz de resolver cuál es la que se corresponde a la de la petición del
servicio provocando un error de aplicación 500 poco descriptivo.

Este Bug ya lo he solucionado, a  través de sobrescribir el método CreateServiceHost (http://msdn.microsoft.com/en-us/library/system.servicemodel.activation.servicehostfactory.createservicehost.aspx) de WebScriptServiceHostFactory,  que es el que se encarga de establecer las
baseaddress. Haciendo que cuando existan varias peticiones, tu establezcas cual
es la que te interesa.

La explicación de la solución
de dicho BUG son dos simples pasos:

Le decimos a
nuestro servicio que su Factory, ahora será nuestro CustomHostFactory

<%@ ServiceHost Language=”C#”
Debug=”true”
Service=”Portal.WebIU.WebServices.SilverlightServices.Servicio.Servicio1″
CodeBehind=”Servicio1.svc.cs”
Factory=”Portal.WebIU.AppCode.CustomHostFactory”
%>

2º Creamos nuestro CustomHostFactory
y s
obrescribimos el
método
CreateServiceHost:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.ServiceModel.Activation;

using System.ServiceModel;

namespace Portal.WebIU.AppCode

{

    public class CustomHostFactory : WebScriptServiceHostFactory

    {

        protected
override ServiceHost
CreateServiceHost(Type serviceType,  Uri[]
baseAddresses)

        {

            Uri[]
newUri;

            if
(baseAddresses.Length > 1)

                newUri = new Uri[]

{ new Uri(“URL/Servicio1.svc”)};

            else
{ newUri = new Uri[]
{ baseAddresses[0] };}

            return
base.CreateServiceHost(serviceType,
newUri);

        }

    }

}

 

Como comentaba, se
supone que este bug ya está arreglado para servidores con IIS7 y FW4.0 (repito
lo de, se supone)

4.2 Acceso Bridge:

Otra arquitectura posible es la de utilizar un servicio
puente, a través del cual obtengamos el HTTPContext para después llamar a X
servicios que pudieran estar fuera del entorno de la aplicación como se muestra
en la Ilustración 4

 

Ilustración 4

 Los problemas que
pudieran derivarse de esta opción son:

                             a)      Política de CrossDomain (si se
configura bien, no debería ser un problema):

Para que el control Silverlight
tenga acceso a los servicios web externos, estos deberán tener como mínimo una política
de CrossDomain, permitiendo el acceso a aquellas peticiones que vengan desde el
dominio de PortalMediadores.

                              b)      Servicios Securizados: Estos servicios
externos, estarían expuestos en principio, de manera publica, de tal manera que
cualquiera que conozca las direcciones de los mismos podrá llamarlos y obtener 
información sensible sin ningún problema. Para ello habría que encriptar
información, establecer algún proxy, firewalls de acceso, o establecer otras políticas
de securización de WCF.

5. Arquitectura recomendada

Atendiendo a lo explicado anteriormente se recomienda una
arquitectura de acceso directo por los siguientes motivos:

         Seguridad:

En el acceso directo, la conexión siempre
viene securizada a través del provider de autenticación del portal, por lo que
se pueden evitar ciertos usos fraudulentos del portal.

            Centralización de despliegue:

Al desplegar todo en un mismo site, los
posibles problemas o incidencias estarán mucho más centralizados, y se evitará
el acoplamiento entre distintos servidores. Lo que para mi es una ventaja a la
hora de desplegar servicios que van a ser consumidos únicamente por un site

        Recomendaciones “oficiales”:

http://msdn.microsoft.com/en-us/library/dd560702%28VS.95%29.aspx

P.D: Que conste que esta fue una solución de ida de hoya personal, por lo que es posible que sea un poco “ñapa”, “estupided” o vete tu a saber… pero en las aplicaciones en las que ha sido implementada, puedo garantizar, que a funcionado a la perfección. Se agradecerá cualquier correción / aportación sobre otras posibles soluciones 🙂

 

4 thoughts on “Access HttpContext from Silverlight

  1. Muy bueno!.
    Teniendo estos bindings (varios baseAddress) con ASP.NET 4.0 existe el Atributo multipleSiteBindingsEnabled:

    Bindings (type:hostname:port)
    http:””:49759
    https:premediadores.zzz.es:443
    http:premediadores.zzz.es:80

    <!–

    –>

    Y el HostFactory:

    class CustomServiceHost : ServiceHost
    {
    public CustomServiceHost(Type serviceType, params Uri[] baseAddresses)
    //: base(serviceType, GetBaseAddresses())
    : base(serviceType, baseAddresses)
    { }

    PD: En mi caso, hay otro problema al llamar a https:premediadores.zzz.es/v2/Service.svc se transforma a https:preiisw01.zzz.es/v2/Service.svc

    Saludotes.

Leave a Reply

Your email address will not be published. Required fields are marked *