#209 Introducing Devise
- Download:
- source codeProject Files in Zip (156 KB)
- mp4Full Size H.264 Video (20.9 MB)
- m4vSmaller H.264 Video (12.6 MB)
- webmFull Size VP8 Video (30.8 MB)
- ogvFull Size Theora Video (28.7 MB)
A lo largo de todos estos episodios hemos cubierto diferentes soluciones de autenticación y hoy veremos otra más llamada “Devise” que últimamente viene ganando popularidad. Devise está basado Warden, una solución de autenticación para Rack y de hecho lo usa por debajo pero no es necesario tener conocimientos de ningún tipo acerca de Warden para seguir este episodio porque vamos a tratar con Devise directamente.
Devise gestiona la autenticación a todos los niveles. Si ya
conocemos Authlogic (que vimos sen el episodio 160 [verlo, leerlo]
sabremos que cubre la capa del modelo. Por el contrario Devise es un
engine de Rails y cubre también vistas y controladores. La
arquitectura de Devise es modular y consta de once módulos cada uno de
los cuales cubre un aspecto diferente de la autenticación. Por
ejemplo el módulo Rememberable
recuerda la autenticación
del usuario en una cookie mientras que otro módulo,
Recoverable
, se ocupa de reiniciar la clave del usuario
enviando instrucciones por correo. Este enfoque hace que sea muy
fácil escoger exactamente las funcionalidades de autenticación que
queramos utilizar.
Añadiendo autenticación a una aplicación
Veamos qué necesitamos para que Devise funcione en una aplicación.
A continuación se muestra una captura de pantalla de una aplicación
sencilla de gestión de proyectos, escrita en Rails 3.0, sobre la que
usaremos Devise para añadir un modelo User
y su
correspondiente autenticación.
Aunque Devise funciona bastante bien con Rails 3, hay una serie de instrucciones específicas de instalación que hay que seguir para instalar la versión correcta, que debería ser la versión actual (1.1.rc0). Si queremos usar Devise con aplicaciones Rails 2.3 necesitamos instalar la versión 1.0.6 porque la versión 1.1 no es compatible hacia atrás con Rails 2.
Dado que estamos trabajando con una aplicación Rails 3, añadiremos
una referencia a Devise en el fichero Gemfile
de nuestra
aplicación teniendo cuidado de especificar la versión correcta.
gem 'devise', '1.1.rc0'
Una vez hecho esto ejecutaremos bundle
para instalar
la gema y sus dependencias
bundle install
El siguiente paso es ejecutar el generador de instalación.
$ rails generate devise_install create config/initializers/devise.rb create config/locales/devise.en.yml =============================================================================== Some setup you must do manually if you haven't yet: 1. Setup default url options for your specific environment. Here is an example of development environment: config.action_mailer.default_url_options = { :host => 'localhost:3000' } This is a required Rails configuration. In production is must be the actual host of your application 2. Ensure you have defined root_url to *something* in your config/routes.rb. For example: root :to => "home#index" ===============================================================================
Este comando genera un par de ficheros: un inicializador y un
archivo con traducciones que contiene todos los mensajes que Devise
necesita mostrar. Tras esto aparecen dos pasos manuales de
instalación que deben seguirse. El primero es configurar la opción
host
para el mailer de la aplicación, mientras
que el segundo indica que debemos tener una ruta raíz. Como en
nuestro caso la aplicación ya tiene una ruta raíz no vamos a tener que
hacer nada, pero sí que vamos a tener que seguir el primer paso. Lo
haremos copiando la línea que aparece en las instrucciones anteriores
al bloque correspondiente el fichero de configuración de
desarrollo.
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
Con esta línea configuramos la opción host
a
locahost
. Cuando vayamos a poner la aplicación en el
entorno de producción tendremos que poner el valor a nuestro nombre de
dominio en el archivo production.rb
.
Creación de modelo de usuario con Devise
Para la autenticación necesitamos un modelo User
, y
Devise proporciona un generador para hacer justo eso. Su uso no
es imprescindibile pero nos ahorrará un par de pasos.
$ rails generate devise User invoke active_record create app/models/user.rb invoke test_unit create test/unit/user_test.rb create test/fixtures/users.yml inject app/models/user.rb create db/migrate/20100412200407_devise_create_users.rb route devise_for :users
Este generador crea varios elementos interesantes: un fichero con
el modelo, una migración y una ruta devise_for
. Pasemos
a verlo todo.
El fichero de modelo generado tene este aspecto:
class User < ActiveRecord::Base # Include default devise modules. Others available are: # :token_authenticatable, :lockable, :timeoutable and :activatable # :confirmable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable # Setup accessible (or protected) attributes for your model attr_accessible :email, :password, :password_confirmation end
El modelo User
es parecido a cualquier otro modelo
ActiveRecord pero tiene una llamada al método devise
, que
es donde sucede la magia de la autenticación. El método
devise
recibe como argumentos una lista de los módulos
que queremos que sean soportados en nuestra aplicación, en nuestro
ejemplo vemos los módulos que veíamos antes:
:rememberable
y :recoverable
. Es fácil
añadir o quitar módulos de esta lista, con lo que personalizaríamos la
funcionalidad de autenticación de Devise para ajustarla a las
necesidades de nuestra aplicación. Por ejemplo hemos quitado
:confirmable
porque no queremos que nuestros usuarios
tengan que confirmar su correo.
Obsérvese que la clase User
también tiene un método
attr_accesible
que enumera los atributos que el usuario
puede cambiar desde la aplicación. Si en nuestro modelo necesitamos
poner nuestras propias columnas también las añadiríamos aquí.
Veamos ahora el fichero de migración que ha sido generado automáticamente.
class DeviseCreateUsers < ActiveRecord::Migration def self.up create_table(:users) do |t| t.database_authenticatable :null => false # t.confirmable t.recoverable t.rememberable t.trackable # t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both t.timestamps end add_index :users, :email, :unique => true # add_index :users, :confirmation_token, :unique => true add_index :users, :reset_password_token, :unique => true # add_index :users, :unlock_token, :unique => true end def self.down drop_table :users end end
La particularidad de esta migración es que invoca sobre la tabla un
método diferente para las columnas que necesita cada módulo. Como no
queremos usar el módulo confirmable
hemos comentado su
método correspondiente y también hemos borrado el índice relativo al
token de confirmación porque dicha columna no existirá en la
tabla.
Una vez configurada la migración según los módulos que queremos utilizar podemos ejecutar la migración de base de datos.
rake db:migrate
Por último tenemos la ruta devise_for
que el
generador nos puso en el fichero de rutas. Si ejecutamos rake
routes
podemos las rutas generadas por esa línea de código.
new_user_session GET /users/sign_in {:controller=>"devise/sessions", :action=>"new"} user_session POST /users/sign_in {:controller=>"devise/sessions", :action=>"create"} destroy_user_session GET /users/sign_out {:controller=>"devise/sessions", :action=>"destroy"} POST /users/password(.:format) {:controller=>"devise/passwords", :action=>"create"} user_password PUT /users/password(.:format) {:controller=>"devise/passwords", :action=>"update"} new_user_password GET /users/password/new(.:format) {:controller=>"devise/passwords", :action=>"new"} edit_user_password GET /users/password/edit(.:format) {:controller=>"devise/passwords", :action=>"edit"} POST /users(.:format) {:controller=>"devise/registrations", :action=>"create"} PUT /users(.:format) {:controller=>"devise/registrations", :action=>"update"} user_registration DELETE /users(.:format) {:controller=>"devise/registrations", :action=>"destroy"} new_user_registration GET /users/sign_up(.:format) {:controller=>"devise/registrations", :action=>"new"} edit_user_registration GET /users/edit(.:format) {:controller=>"devise/registrations", :action=>"edit"}
No resulta muy legible pero puede verse que hay varias rutas de autenticación para entrar y salir de la aplicación, reiniciar las claves, darse de alta como nuevo usuario y resetear un perfil. Por supuesto podemos cambiar estas rutas, en caso de que necesitemos cambiarlas.
Ya podemos acceder a nuestra autenticación a través de estas rutas.
Si entramos en /users/sign_up
veremos el siguiente
formulario para registrarnos como nuevo usuario:
Si rellenamos el formulario y pulsamos el botón de “Sign
up” entraremos en la aplicación con sesión iniciada. Podemos
salirnos visitando /users/sign_out
, pero si intentamos
iniciar sesión de nuevo con /users/sign_in
y rellenamos
el formulario con nuestro usuario y clave veremos un error.
Esto se debe a un problema ajeno a Devise y específico de Rails 3.0
beta 2. Pero por suerte es fácil de corregir: en el archivo
/config/initializers/cookie_verification_secret.rb
hay
una línea de código que establece la clave secreta para verificar las
cookies firmadas.
# Be sure to restart your server when you modify this file. # Your secret key for verifying the integrity of signed cookies. # If you change this key, all old signed cookies will become invalid! # Make sure the secret is at least 30 characters and all random, # no regular words or you'll be exposed to dictionary attacks. Rails.application.config.cookie_secret = '3b161f7668f938d1aeb73e1137964f8d5ebaf32b9173c2130ecb73b95b610702b77370640dce7e76700fb228f35f7865ab2a5ccd22d00563504a2ea9c3d8dffe'
Lo único que tenemos que hacer es borrar esta línea y ponerla en /config/application.rb
.
require File.expand_path('../boot', __FILE__) require 'rails/all' Bundler.require(:default, Rails.env) if defined?(Bundler) module ProjectManage class Application < Rails::Application config.filter_parameters << :password config.cookie_secret = '3b161f7668f938d1aeb73e1137964f8d5ebaf32b9173c2130ecb73b95b610702b77370640dce7e76700fb228f35f7865ab2a5ccd22d00563504a2ea9c3d8dffe' end end
Para que este cambio surta efecto tenemos que reiniciar el servidor, pero una vez hecho esto ya deberíamos poder iniciar sesión correctamente.
Ahora que tenemos la autenticación configurada y funcionando podemos empezar a mejorarla. Nos gustaría tener un enlace en la parte superior de la página que nos diga si tenemos sesión iniciada y otro que nos permita iniciar y cerrar una sesión.
Lo podemos hacer modificando el fichero de layout de la aplicación de forma que los enlaces sean visibles en todas las páginas. Añadamos las siguientes líneas justo después del código que muestra los mensajes flash:
<div id="user_nav"> <% if user_signed_in? %> Signed in as <%= current_user.email %>. Not you? <%= link_to "Sign out", destroy_user_session_path %> <% else %> <%= link_to "Sign up", new_user_registration_path %> or <%= link_to "Sign in", new_user_session_path %> <% end %> </div>
En este código tenemos una sentencia
if
/else
para poder mostrar un mensaje
diferente dependiendo de si el usuario que está viendo el sitio tiene
iniciada sesión o no. Esto lo podemos averiguar llamando a un método
que nos proporciona Devise llamado user_signed_in?
y que
devolverá true
si el usuario tiene sesión iniciada, en
cuyo caso queremos mostrar su dirección de correo y un enlace para
cerrar la sesión. Podemos mostrar la dirección de correo del usuario
actual llamando al método current_user
para recuperar el
objeto User
del usuario y mostrar su correo. Para
mostrar la ruta adecuada para el enlace de cierre de sesión podemos
mirar en las rutas generadas con rake routes
, donde
podemos ver destroy_user_session
que mapea a
/users/sign_out
, por lo que para generar la URL correcta
podemos utilizar destroy_user_session_path
.
También podemos utilizar new_user_registration_path
y
new_user_session_path
para crear los enalces para darse
de alta o iniciar seesión. Una vez puestos ya podemos recargar la
página y veremos la información el usuario en la parte superior.
Si seguimos el enlace “Sign out” veremos que ahora aparecen los enlaces “Sign up” y “Sign in” porque no tenemos la sesión iniciada.
Como puede verse, con Devise es muy sencillo armar toda la
funcionalidad de autenticación, añadiendo sin esfuerzo la capacidad de
registrar nuevos usuarios y permitirles iniciar y cerrar sesión.
También hay otras partes que no hemos cubierto, como por ejemplo la
página de reinicio de contraseña. Así mismo, si hubiésemos conservado
el módulo confirmable
Devise también habría generado el
formulario y lógica correspondientes.
Aunque la generación automática de tantos formularios es bastante útil probablemente queramos personalizar su aspecto para ajustarlos a nuestra aplicación. Por suerte esto es fácil de hacer con Devise y lo veremos en el próximo episodio.