Si estamos trabajando en el entorno web, es decir, cuando cliente y servidor son puras tecnologías web y están ejecutándose en el mismo contexto, el servidor determina si el cliente SignalR está autenticado utilizando los mismos mecanismos de siempre, basados en la existencia de la cookie de autorización de ASP.NET y en los datos contenidos en ésta.
De esta forma, si un cliente entra con su navegador a nuestra web y supera el procedimiento de autenticación, a partir de ese momento todas sus peticiones llevarán adjunta la cookie de autorización, y ésta misma es la que usará SignalR para conceder o denegar el acceso a los métodos de los Hubs cuando accedamos a ellos usando el cliente Javascript.
Sin embargo, a través del formulario de contacto de Variable not found, me llega una consulta muy interesante del amigo Juan F.: ¿cómo podemos usar ese mismo atributo
Authorize
para controlar el acceso desde aplicaciones no web, las que usan el cliente genérico .NET de SignalR?0. Solución conceptual
Como hemos comentado, SignalR permite indicar mediante el atributoAuthorize
que un método de un Hub puede ser ejecutado sólo por usuarios autenticados, o incluso especificar cuáles de ellos o sus roles:[Authorize(Users="jmaguilar")]Desde el punto de vista del servidor, la autorización se resuelve observando el
public Task PrivateMessage(string message)
{
return Clients.All.Message(
DateTime.Now.ToLongTimeString() + " -> " + message);
}
IPrincipal
asociado a la conexión física abierta con el servidor, disponible en la propiedad Context.User
de la clase Hub
. A su vez, éste IPrincipal
es rellenado de forma automática por la plataforma a la vista de la información contenida en la cookie de autenticación, que normalmente viaja en las peticiones con el nombre .ASPXAUTH
.Está claro que la solución a la pregunta del amigo Juan pasa por obtener una de estas cookies que autorizan al usuario desde nuestra aplicación cliente .NET, y adjuntarla a la conexión que vamos a realizar al Hub. Se trata, por tanto, de un proceso previo a la apertura de la conexión con el Hub, más o menos con los siguientes pasos:
- Implementamos en el mismo servidor donde se encuentran los Hubs un método/acción/lo que sea capaz de autenticar al usuario y generar la cookie de autorización de ASP.NET.
- Creamos una conexión HTTP desde el cliente invocando a dicho método, al que suministramos las credenciales del usuario.
- Obtenemos la respuesta de dicha petición, y de ella extraemos el valor de la cookie .ASPXAUTH.
- Antes de iniciar la conexión con el Hub, adjuntamos la cookie a la petición para que el servidor nos reconozca.
Authorize
. ¿Algo lioso? Ya verás como no ;-)
1. Implementación de la autenticación (servidor)
Dado que la implementación depende de la tecnología (MVC, Webforms, Web API, WebPages…), vamos a simplificarla al máximo. Simplemente crearemos un archivo llamado Login.aspx, y en su code-behind introduciremos el siguiente código:protected void Page_Load(object sender, EventArgs e)Obviamente, podríamos haber escrito el mismo código para MVC, en el interior de una acción:
{
Response.Clear();
var username = Request["username"];
var password = Request["password"];
// In real code, we could check if this user exists
// in the database.
// For sake of brevity, we'll assume that the credentials
// are valid
FormsAuthentication.SetAuthCookie(username, false);
Response.End();
}
public ActionResult Login(string userName, string password)En cualquiera de los dos casos, el resultado es el mismo: la petición se responderá con un contenido nulo, pero con una bonita cookie que contiene información del usuario autenticado en el sitio web :-)
{
...
// If the credentials are valid:
FormsAuthentication.SetAuthCookie(userName, false);
return new EmptyResult();
}
2. Obtención de la cookie de autenticación (cliente)
Desde la aplicación .NET, a continuación vamos a implementar un método que realice una petición hacia la página .aspx (o la acción MVC, da igual) para validar las credenciales y obtener la cookie si se supera la autenticación. Esto podemos conseguirlo de muchas formas, la plataforma ofrece varias clases para realizar peticiones HTTP, peroWebClient
parece una buena candidata para usar al facilitarnos el acceso directo a las cookies generadas por el servidor:private static Cookie GetAuthCookie(string loginUrl, string user, string pass)Este código podríamos mejorarlo bastante, por ejemplo, usando el verbo POST para enviar las credenciales. Sin embargo, he preferido dejarlo así para no hacerlo más extenso y que no nos distraiga de nuestro objetivo real.
{
var url = loginUrl + "?username=" + user + "&password=" + pass;
var request = WebRequest.Create(url) as HttpWebRequest;
request.CookieContainer = new CookieContainer();
var httpResponse = request.GetResponse() as HttpWebResponse;
var cookie = httpResponse.Cookies[".ASPXAUTH"];
httpResponse.Close();
return cookie;
}
3. Adjuntar la cookie al establecer la conexión con Signalr (cliente)
Ahora llega ya el momento de utilizar el método anterior en el proceso de establecimiento de la conexión del cliente SignalR. Tan sencillo como lo que vemos a continuación:const string host = "http://myserver.com:1234";Como podemos observar, el código es absolutamente trivial. El objeto
var connection = new HubConnection(host);
Console.Write("Enter your username: ");
var username = Console.ReadLine();
Console.Write("Enter your password: ");
var password = Console.ReadLine();
// Get the cookie
var cookie = GetAuthCookie(host+"/login.aspx", username, password);
// Attach the cookie to the connection
connection.CookieContainer = new CookieContainer();
connection.CookieContainer.Add(cookie);
var proxy = connection.CreateHubProxy("EchoHub");
// ... other initialization code
// And, finally, start the connection
connection.Start();
HubConnection
de SignalR dispone de una propiedad llamada CookieContainer
donde podemos establecer las cookies que deseamos viajen al servidor durante el establecimiento de la conexión. Introducimos ahí la cookie de autorización obtenida previamente, y ¡listo!A partir de este momento, ya podremos acceder a todos los métodos o Hubs protegidos mediante el atributo
[Authorize]
, e incluso acceder a ellos si somos los usuarios específicamente autorizados.Si os interesa verlo en acción, podéis descargar un proyecto de demostración desde mi Skydrive.
Publicado en Variable not found.