May 03, 2005

Autentificación de usuarios con FlashRemoting y setCredentials

A lo mejor algunas personas han escuchado hablar acerca de un método en Flash Remoting llamado setCredentials, y si no, pues vamos a explicarlo un poco. Antes les recomiendo leer este tutorial si no conoce mucho acerca de Remoting.
Flash Remoting para cuestiones de seguridad maneja un sistema interno basado en sesiones. Generalmente cuando nosotros desarrollamos un sitio seguro, requerimos ir pasando el identificador de sesión por cada petición a un archivo que requiera autentificacion. A veces usamos cookies, a veces usamos variables $_GET y el lenguaje del server side generalmente hace el resto automaticamente. El problema de las cookies es que en ocasiones puede que estén deshabilitadas en el navegador. En el caso de las variables $_GET puede llegar a ser tedioso tener que concatenar el identificador con los links en nuestra aplicación. De igual manera, en Flash, cuando hacemos peticiones vía LoadVars o XML, tenemos que elegir alguna de las dos opciones (o las dos) para hacer segura nuestra aplicación. En Flash Remoting, tenemos la ventaja de que existe un sistema nativo tremendamente fácil de utilizar e implementar (y curiosamente por mas fácil que este, hay muy poca documentación acerca de ello). Esto es mediante los setCredentials. La idea es la siguiente : creamos nuestro servicio en Flash, validamos un usuario mediante setCredentials y cada vez que queramos invocar un método que requiera autentificación de nuestra clase, se enviara automaticamente el identificador de sesión (sin que nosotros tengamos que preocuparnos por conocer tal numero). Entonces Flash Remoting nos devolverá o un ResultEvent o un FaultEvent dependiendo si la validación fue correcta o no. Previamente nosotros deberemos definir un servicio llamado _authentificate (en amfphp) que se invocara automaticamente cada vez llamemos al setCredentials. Este método deberá devolver "admin" en caso de que la validación sea correcta. Aquí, extendiendonos a un ejemplo mas real, podemos hacer una conexión a una base de datos donde tengamos registrados a nuestros usuarios. Veamos el siguiente código :

<?PHP

class SecuredConn {

function SecuredConn () {
$this->methodTable = array (
"welcome" => array(
"description" => "validate function",
"access" => "remote",
"roles" => "admin",
"arguments" => array()
)
);
}

function _authenticate($user, $password) {
if ($user == "daniel" && $password == "asfusion") {
return "admin";
}
else {
return false;
}
}
function welcome () {
return "bienvenido!!!!"
}
}
?>


También es importante notar, que dentro de la tabla de métodos, específicamente en el array welcome, hay una propiedad llamada roles, y seteandola como admin provoca que se requiera previamente validar al usuario mediante setCredentials.

En flash tenemos lo siguiente :

import mx.remoting.Service;
import mx.services.Log;
import mx.rpc.RelayResponder;
import mx.rpc.FaultEvent;
import mx.rpc.ResultEvent;
import mx.remoting.PendingCall;

var sSecured:Service = new Service ("gateway.php", null,"SecuredConn",null,null);
sSecured.connection.setCredentials ("daniel", "asfusion");

function welcomeResult (rs:ResultEvent) {
trace (rs.result);
}

function welcomeFault (fl:FaultEvent) {
trace("Su usuario no esta validado");
trace("Hubo un problema: "+fl.fault.faultstring);
trace("El código del error es: "+fl.fault.faultcode);
trace("Detalles: "+fl.fault.detail);
}

var pcWelcome:PendingCall = sSecured.welcome ();
pcWelcome.responder = new RelayResponder (this, "welcomeResult", "welcomeFault");


En este caso, veremos un mensaje en la ventana del output inidicandonos que el servicio fue llamado correctamente. Si comentamos la linea de los setCredentials, veremos que el servicio no fue invocado correctamente y nos devolverá un error indicandonos que no existe una variable $_SESSION (que es la que administra las sesiones en php). La ventaja de utilizar setCredentials es que si ademas de querer restringir ciertos métodos, es una manera fácil de hacerlo, ya que nosotros no tenemos que andar validando a cada rato.

Una recomendación muy importante, es que cada vez que queramos usar un servicio diferente (ojo: hablo de servicios, no de métodos), habrá que invocar nuevamente el setCredentials. O sea... estoy usando el servicio Noticias, con x numero de métodos. Me logueo con setCredentials y listo, no importa el numero de métodos que invoque, con el primer setCredentials me va a validar el usuario. Posteriormente quiero utilizar el servicio Estadísticas, entonces tengo que volver a registrarme con setCredentials. Un tip interesante es que para no tener que volver a escribir a cada rato el método _authentificate en sus servicios, les recomiendo que utilicen herencias de php. Por ejemplo, nuestro servicio anterior será la superclase de este servicio :

include_once("SecuredConn.php");

class News extends SecuredConn {

function News {
$this->methodTable = array (
"getNews" => array (
"description" => "Devuelve el listado de noticias",
"access" => "remote",
"roles" => "admin",
"arguments" => array ()
)
);
}

function getNews () {
return "AsFusion es un blog mas en la blogosfera que intenta sobrevivir en este mundo cruel";
}
}


La ventaja de este método, es que si por alguna razón mejoramos nuestro sistema de validación de usuarios (o bien, conectamos con una base de datos), nada mas habría que cambiar el método _authentificate en SecuredConn. Es importante señalar que esto nos ahorra mucho trabajo si estamos trabajando como varios servicios que requieran validación dentro de una aplicación.

Ademas, es posible que nosotros necesitemos dentro de nuestro método _authentificate agregar mas de dos argumentos, que es la cantidad limite que acepta, pongas los que les pongas al método _authentificate, ya que los componentes de Remoting de Flash únicamente envía dos argumentos, . Por ahí encontré en los foros de www.sephiroth.com un tip interesante : Utilizar el argumento de password como si fueran dos. Podemos separar ambos argumentos utilizando un caracter como #. A que me refiero:

sSecured.connection.setCredentials ("daniel", "asfusion#true");

En nuestro método del servicio únicamente lo que hacemos es "rebanar" el string utilizando split() por ejemplo. Otra manera, es pasandole un array (el cual me parece mas seguro) dentro de password o username.

Ya por ultimo, para desloguearse pueden hacer que se envíen los setCredentials en blanco, o utilizar el método logout que previamente han de definir en su servicio:

function logout () {
Authenticate::logout();
}


Ojalá les ayude este pequeño tutorial. Saludos!


Comentarios

Por cierto...un agradecimiento a Maikel por descubrir que me faltaba poner el código de la clase SecuredConn

Saludos!

Algo que no comentas y es que para colocar varios roles, deben estar separados por comas. Algo así:
"roles" => "admin, moderador, user",

Saludos