Ruby on Rails, parte 10: Cookies y sesiones

2 Jun

¡Buenas! Hasta ahora hemos visto cómo enviar datos desde una página a otra, ya sea con una url (método GET) o con variables ocultas en la cabecera (método POST) Sin embargo en una aplicación web se requiere de mantener datos guardados durante la navegación del usuario por la página. Esto puede conseguirse gracias a las cookies y a las sesiones.

 

cookies

Las cookies son pequeños archivos que se guardan en navegador del usuario. Nuestra aplicación puede guardarlos en el cliente, borrarlos o consultar si existen o no.

Para ello crearemos un controlador que se llame test_cookies y una vista llamada identificacion:

rails g controller test_cookies identificacion

Una vez creada iremos a nuestro proyecto a config -> routes.rb y editaremos este archivo. Buscamos la línea:

get "test_cookies/identificacion"

Y vamos a agregarle a esta acción la posibilidad de acceder por POST. Así que debajo agregamos:

post "test_cookies/identificacion"

Esto entonces debería quedar:

get "test_cookies/identificacion"
post "test_cookies/identificacion"

Bien, ahora vamos al controlador y a la vista. Entramos en app y abrimos dentro de controller test_cookies_controller.rb y luego vamos a la carpeta de views y abrimos dentro del directorio test_cookies la única vista que hay que es identificacion.html.erb.

Dentro de nuestra vista ingresaremos esto:

<% if @nombre %>
   <h2> Bienvenido/a <%= @nombre %> </h2>
   <%= form_tag({:controller => "test_cookies", :action => "identificacion"}, :method => "post") do %>
      <%= submit_tag "Quiero borrar mi identidad." %>
   <% end %>
<% else %> 
   <h2> Bienvenido/a anónimo </h2>
   <p> Disculpe, pero uste aun no se ha identificado, por favor ingrese su nombre. </p>
   <%= form_tag({:controller => "test_cookies", :action => "identificacion"}, :method => "post") do %>
      <%= text_field "nombre", "", :required => "required" %>
      <%= submit_tag "Identificarse." %>
   <% end %>
<% end; %>

La vista primera verifica si existe un nombre, hablando en criollo, si el usuario se ha identificado, si ha creado la cookie. Si se identificó, entonces saludará con el nombre y mostrará un botón de formulario para eliminar la identificación.

Si en cambio no está identificado saludará como anónimo y dará la posibilidad de enviar un formulario para identificar al usuario con un nombre.

Ahora vamos a la parte del controlador, el método identificacion debería quedar así:

def identificacion
   #Preguntamos si hay un envío de formulario ya sea para crear o eliminar la cookie.
   if request.post?
      if params[:nombre].present?
         #Se está intentando crear la cookie.
         cookies[:nombre] = {
            :value => params[:nombre],#Valor de la cookie.
            :expires => 1.year.from_now,#Tiempo antes de expirar (En este caso 1 año)
            :domain => :all#En qué partes de nuestra página estará disponible (En este caso en todo el sitio)
         }
      else
         #Se está intentando eliminar la cookie.
         cookies.delete(:nombre, :domain => :all);
      end;
      #Redirecciona al controlador.
      redirect_to :controller => "test_cookies", :action => "identificacion";
   end
   #Si existe la cookie con el nombre del visitante la guardamos en la variable @nombre.
   if defined?(cookies[:nombre])
      @nombre = cookies[:nombre];
   else
      @nombre = false;
   end
 end

Aquí por empezar pregunta si hay una petición POST, esto puede darse por dos motivos, el primero es porque el usuario está intentando identificarse o borrar la identificación. Si está intentando identificarse (se comprueba si existe una variable POST nombre) entonces se creará un cookie :nombre con el valor que ha ingresado el usuario, el tiempo que durará esa cookie y en qué partes del sitio estará disponible. Pero si en cambio está intentando eliminar la identificación se borrará la cookie con el método delete().

Si en cambio no ha habido ninguna petición POST, preguntará si existe una cookie llamada :nombre, si existe guardará el valor en una variable @nombre, sino la dejará en false. De esta manera la vista podrá comprobar si el usuario está identificado o no.

 

Sesiones

La sesiones a diferencia de los cookies guardan información pero en el servidor, mientras que el navegador se guarda una cookie con una cadena de identificación para vincular al servidor con el navegador. Esto es más seguro que las cookies, ya que las sesiones sólo pueden modificar su valor desde el servidor.

Para ello vamos a crear un controlador llamado test_sesiones con tres acciones iniciar_sesion, cerrar_sesion y bienvenida. Así que escribimos en la consola:

rails g controller test_sesiones iniciar_sesion cerrar_sesion bienvenida

iniciar_sesion será un formulario de logueo para que el usuario inicie sesión, si el logueo es incorrecto mostrará un mensaje por pantalla y si es correcto redireccionará a la acción bienvenida que a su vez será una acción que sólo podrá ser accedida si el usuario se ha logueado correctamente. Mientras tanto la acción cerrar_sesion servirá para desloguear al usuario.

Para ello vamos a crear tres métodos privados nuevos en la clase controlador que acabamos de crear. Para definir métodos privados debemos usar la palabra private y debajo los métodos que serán privados. Nuestra clase quedará así:

class TestSesionesController < ApplicationController
   def iniciar_sesion
   end
   def cerrar_sesion
   end
   def bienvenida
   end
   #Métodos privados.
   private
   def login(usuario, contrasena)
      #Verifica que el nombre de usuario y la contraseña sean correctos
      if usuario == "administrador" and contrasena == "1234"
         #Los datos son correctos entonces crea la sesión y devuelve true.
         session[:logueado] = true;
         session[:nombre] = "Cosme";
         session[:apellido] = "Fulanito";
         return true;
      else
         #Los datos no son correctos entonces devuelve false.
         return false;
      end
   end
   def logout
      #Desloguea al usuario.
      session[:logueado] = false;
      session[:nombre] = nil;
      session[:apellido] = nil;
   end
   def get_login
      #Verifica si el usuario está logueado. Primero pregunta si existe la session[:logueado] y además que este sea true, si existe devuelve la sesión sino existe devuelve false.
      if defined?(session[:logueado]) and session[:logueado]
         #Está logueado.
         return session;
      else
         #No está logueado.
         return false;
      end
   end
end

Bien, acá tenemos tres métodos privados, los métodos privados son aquellos que sólo pueden ser accedidos dentro de la clase, supongo que muchos ya lo tienen claro esto. En los controladores de Rails los métodos públicos, osea los que se crean para cada acción, pueden utilizar métodos privados de ayuda para resolver operaciones que requieren varias líneas de código.

En este caso nosotros creamos tres métodos privados, uno para crear una sesión, otro para cerrarla y el último para verificar si existe o no una sesión ya creada.

Antes de comenzar con la acción iniciar_sesion vamos a nuestro archivos routes.rb en config -> routes.rb y agregamos una acción por post. Buscamos:

get "test_sesiones/iniciar_sesion"

Y agregamos debajo:

post "test_sesiones/iniciar_sesion"

Así que debería quedar:

get "test_sesiones/iniciar_sesion"
post "test_sesiones/iniciar_sesion"

Comencemos a editar la vista de esta acción, dentro de app -> views -> test_sesiones -> iniciar_sesion.html.erb y editamos este archivo:

<h2> Iniciar sesión </h2>
<%= form_tag({:controller => "test_sesiones", :action => "iniciar_sesion"}, :method => "post") do %>
   <%= label_tag "nombre_usuario", "Nombre de usuario" %>
   <br />
   <%= text_field_tag "nombre_usuario", "", :required => "required" %>
   <br />
   <%= label_tag "contrasena", "Contraseña" %>
   <br />
   <%= password_field_tag "contrasena", "", :required => "required" %>
   <br />
   <%= submit_tag "Ingresar" %>
<% end %>
<% if @error_login %>
 <div style="color: #f00;"> El nombre de usuario o la contraseña son incorrectos. </div>
<% end %>

Habrá un formulario para loguearse, con nombre de usuario y contraseña y debajo habrá un div que se mostrará si el servidor después de enviar el formulario nos devuelve un error de logueo.

Ahora vamos a editar el método iniciar_sesion de esta manera:

def iniciar_sesion
   @error_login = false;
   #Verifica si se ha enviado el formulario.
   if request.post?
      #Verifica si el nombre de usuario y la contraseña son correctos.
      if login(params[:nombre_usuario], params[:contrasena])
         #Los datos son correctos así que redirecciona a la página de bienvenida.
         redirect_to :controller => "test_sesiones", :action => "bienvenida";
      else
         #Los datos son incorrectos así que setea la variable @error_login a true para mostrar el error por pantalla.
         @error_login = true;
      end
   end
 end

Una vez enviado el formulario por POST se pasará a validar si el nombre de usuario y la contraseña son correctos. Al no trabajar con base de datos (por ahora) vamos a ‘hardcodear’ los datos para que el nombre de usuario sea ‘administrador’ y la contraseña ‘1234’. Si los datos que ingresó el usuario son esos, entonces creará la sesión y redireccionará a la página de bienvenida, pero de no ser así igualará una variable @error_login a true para mostrar el error en la vista.

Ahora vamos a modificar el método bienvenida:

def bienvenida
   @sesion = get_login();
   if @sesion
      @nombre = @sesion[:nombre];
      @apellido = @sesion[:apellido];
   else
      redirect_to :controller => "test_sesiones", :action => "iniciar_sesion";
   end
 end

En primer medida verifica que el usuario esté logueado. Si lo está, creará una variable @nombre y otra @apellido para mostrarlas en la vista, pero si el usuario no está logueado lo redireccionará a la pantalla con el formulario de inicio de sesión.

Ahora vamos a la vista de esa acción, en bienvenida.html.erb y copiamos esto:

<h2> Bienvenido/a <%="#{@nombre} #{@apellido}"%> </h2>
<p> <%= link_to "Cerrar sesión", :controller => "test_sesiones", :action => "cerrar_sesion" %> </p>

Acá simplemente nos mostrará el nombre y apellido del usuario y un link para cerrar la sesión que apuntará a la acción cerrar_sesion.

Sigamos, vamos a esta acción a cerrar_sesion. Modificamos el método del controlador por:

def cerrar_sesion
   @sesion = get_login();
   if @sesion
      logout();
   else
      redirect_to :controller => "test_sesiones", :action => "iniciar_sesion";
   end
 end

El código es muy simple, si existe una sesión creada la cierra, sino redirecciona a la página de login. Pero antes vamos a detenernos en una cosa. Si le prestaste atención al método privado que creamos antes logout() seguramente habrás notado que la forma que yo utilizo para cerrar sesión es:

session[:logueado] = false;
session[:nombre] = nil;
session[:apellido] = nil;

Sin embargo, en realidad yo no estoy cerrando la sesión, la sesión sigue existiendo, yo sólo le seteo false a :logueado y nil a :nombre y :apellido. Pero en realidad la sesión sigue existiendo, osea esas variable siguen en memoria. Si lo que en cambio querés es eliminar la sesión de memoria deberías usar:

reset_session;

Esto directamente eliminará todas las variables ya sea :logueado, :nombre y :apellido. La razón de por que yo no uso reset_session es porque yo de pronto podría tener otras variables para hacer por ejemplo un seguimiento al visitante, pero de usar reset_session perderé todos los datos en cuanto el usuario cierre la sesión.

La vista cerrar_sesion.html.erb quedará así:

<h2> La sesión ha sido cerrada. Hasta la próxima. </h2>
<p> <%= link_to "Volver a ingresar", :controller => "test_sesiones", :action => "iniciar_sesion" %> </p>

Con el mensaje de que se ha cerrado la sesión y un link por si el usuario quiere volver a ingresar.

Anterior: Ruby on Rails, parte 9: Subir y trabajar con archivos

Siguiente: Ruby on Rails, parte 11: Conexión con MySQL

Redes sociables

    4 thoughts on “Ruby on Rails, parte 10: Cookies y sesiones

    1. Hoy he encontrado en San Google este excelente Blog, a favoritos y a explorar mas contenido de este.

      Muchas Gracias.

      • Sí, Google lo sabe todo.

        Gracias a vos por pasarte. Cualquier cosa por acá me preguntás.

        Saludos!

    Comments are closed.