Access HttpContext from Silverlight

Published 25/1/2011 9:32 | Isaac Fernandez

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 sobrescribimos 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 :)

 

Comparte este post:

Comentarios

# Mejor alquilar que comprar said on January 25, 2011 11:14 AM:

Gran articulo.

Un saludo.

# Luis Miguel Blanco said on January 25, 2011 6:44 PM:

Hola Isaac

Felicidades por el artículo, está fenomenal. Eres una máquina ;-).

Saludotes,

Luismi

# Isaac Fernandez said on January 26, 2011 8:34 AM:

Gracias por las aportaciones y comentarios :D