Explorando Devise, Parte 1
Spanish (Español) translation by Rafael Chavarría (you can also view the original English article)
En algunos de mis tutoriales anteriores sobre carga de imágenes en Rails, hice mención de Devise pero no me adentré mucho a este. En este tutorial, te estaré enseñando sobre Devise.
¿Listo? ¡Comencemos!
Introducción y Módulos de Devise
Devise es una solución de autenticación para Rails construida con Warden y proporcionada por la increíble gente en Plataformatec. Devise proporciona diferentes módulos:
- Database Authenticatable: esto encripta y almacena una contraseña a la base de datos para validar la autenticidad de un usuario mientras inicia sesión.
- Omniauthable: Esto adjunta soporte OmniAuth a Devise. Los usuarios de tu aplicación podrán iniciar sesión usando cuentas como Facebook, Twitter, y Google.
- Confirmable: Esto habilita el envío de emails con instrucciones que ayudarán a la verificación de una cuenta.
- Recoverable: Este módulo ayuda en tiempos cuando los usuarios olvidan su contraseña y necesitan recuperarla. Con esto, el usuario será capaz de reiniciar su contraseña.
- Registerable: Esto maneja el registro de usuarios. También permite a los usuarios editar y borrar sus cuentas.
- Rememberable: Este modulo hace posible para tu aplicación recordar a un usuario con sesión iniciada almacenando una cookie.
- Trackable: Este módulo ayuda a llevar la cuenta de inicio de sesión, estampas de tiempo y direcciones IP.
- Timeoutable: Este módulo es responsable de expirar una sesión que no ha estado activa por un periodo de tiempo.
- Validatable: Con este módulo, se validan el email y la contraseña.
- Lockable: Esto proporciona una capa extra de seguridad---cuando se activa, una cuenta puede ser bloqueada después de un número dado de intentos de sesión fallidos.
Integración Devise
Para el propósito de este tutorial, vamos a generar una aplicación Rails que usaremos para revisar el funcionamiento de Devise. ¡Procedamos!
rails new devise-app -T
La bandera -T le dice a Rails que genere la aplicación sin la suite de prueba por defecto. Navega a tu directorio de aplicación y agrega los siguientes gems a tu Gemfile.
1 |
#Gemfile |
2 |
|
3 |
gem 'devise', '~> 4.1' |
4 |
gem 'bootstrap-sass', '~> 3.3' |
Ahora instala los gems Devise y Bootstrap que acabas de agregar.
bundle install
Renombra tu archivo app/assets/stylesheets/application.css a app/assets/stylesheets/application.scss y agrega las siguientes líneas en el:
1 |
#app/assets/stylesheets/application.scss |
2 |
|
3 |
@import "bootstrap-sprockets"; |
4 |
@import "bootstrap"; |
Abre el archivo app/assets/javascripts/application.js y requiere bootstrap-sprockets. El mío luce así:
1 |
#app/assets/javascripts/application.js |
2 |
|
3 |
//= require jquery |
4 |
//= require bootstrap-sprockets |
5 |
//= require jquery_ujs |
6 |
//= require turbolinks |
7 |
//= require_tree . |
Después, necesitas ejecuta el comando Rails para instalar los archivos de configuración para Devise. Lo haces ejecutando este comando:
rails generate devise:install
El comando genera lo siguiente en tu terminal. Deberías leerlo para entender lo que sucedió.
1 |
create config/initializers/devise.rb |
2 |
create config/locales/devise.en.yml |
3 |
=============================================================================== |
4 |
|
5 |
Some setup you must do manually if you haven't yet: |
6 |
|
7 |
1. Ensure you have defined default url options in your environments files. Here |
8 |
is an example of default_url_options appropriate for a development environment |
9 |
in config/environments/development.rb: |
10 |
|
11 |
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
|
12 |
|
13 |
In production, :host should be set to the actual host of your application. |
14 |
|
15 |
2. Ensure you have defined root_url to *something* in your config/routes.rb. |
16 |
For example: |
17 |
|
18 |
root to: "home#index" |
19 |
|
20 |
3. Ensure you have flash messages in app/views/layouts/application.html.erb. |
21 |
For example: |
22 |
|
23 |
<p class="notice"><%= notice %></p> |
24 |
<p class="alert"><%= alert %></p> |
25 |
|
26 |
4. If you are deploying on Heroku with Rails 3.2 only, you may want to set: |
27 |
|
28 |
config.assets.initialize_on_precompile = false |
29 |
|
30 |
On config/application.rb forcing your application to not access the DB |
31 |
or load models when precompiling your assets. |
32 |
|
33 |
5. You can copy Devise views (for customization) to your app by running: |
34 |
|
35 |
rails g devise:views |
36 |
|
37 |
=============================================================================== |
El comando también genera dos archivos, que puedes encontrar en el directorio config. También nos da algunas instrucciones sobre lo que deberíamos hacer.
Navega a tu diseño de aplicación, app/views/layouts/application.html.erb, y hazlo lucir como lo que tengo abajo:
1 |
#app/views/layouts/application.html.erb |
2 |
|
3 |
<!DOCTYPE html>
|
4 |
<html>
|
5 |
<head>
|
6 |
<title>DeviseApp</title> |
7 |
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> |
8 |
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> |
9 |
<%= csrf_meta_tags %> |
10 |
</head>
|
11 |
<body>
|
12 |
<div class="container-fluid"> |
13 |
<p class="notice"><%= notice %></p> |
14 |
<p class="alert"><%= alert %></p> |
15 |
</div>
|
16 |
|
17 |
<div class="container-fluid"> |
18 |
<%= yield %> |
19 |
</div>
|
20 |
|
21 |
</body>
|
22 |
</html>
|
Necesitas definir las opciones de URL por defecto para tu entorno de desarrollo. Agrega el código de abajo en config/environments/development.rb.
1 |
#config/environments/development.rb |
2 |
|
3 |
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
|
Ahora necesitas crear un modelo User para Devise. Puedes hacerlo usando tu terminal.
rails generate devise User
Esto generará un archivo user.rb en tu directorio app/models. El archivo generado lucirá así:
1 |
class User < ActiveRecord::Base |
2 |
# Include default devise modules. Others available are: |
3 |
# :confirmable, :lockable, :timeoutable and :omniauthable |
4 |
devise :database_authenticatable, :registerable, |
5 |
:recoverable, :rememberable, :trackable, :validatable |
6 |
end
|
Puedes ver que contiene los módulos por defecto que mencioné arriba. El comando que ejecutaste también modificó tu archivo config/routes.rb agregando una ruta para devise. Deberías revisar eso.
En este punto, necesitas migrar tu base de datos. Lo haces ejecutando:
rake db:migrate
Autenticación Usando Devise
Ahora necesitas crear un PagesController y envolver la Autenticación Devise alrededor de este---Esto prevendrá a personas no autorizadas de ver la página.
rails generate controller Pages index
Abre tu archivo de rutas y establece la raíz de tu aplicación.
1 |
#config/routes.rb |
2 |
|
3 |
Rails.application.routes.draw do |
4 |
devise_for :users |
5 |
root to: "pages#index" |
6 |
end |
Abre tu PagesController y agrega autenticación para tus páginas index y new.
1 |
#app/controllers/pages_controller.rb |
2 |
|
3 |
class PagesController < ApplicationController |
4 |
before_action :authenticate_user!, only: [:index, :new] |
5 |
|
6 |
def index |
7 |
end
|
8 |
|
9 |
def new |
10 |
end
|
11 |
end
|
El código muestra que las páginas index y new son accesibles solo para usuarios registrados. Abre tu terminal y comienza tu rails sever. Apunta tu navegador a http://localhost:3000 y serás redireccionado automáticamente a la página de inicio de sesión de Devise.
Iniciando Sesión Sin Usar Email
Los medios por defecto para iniciar sesión en Devise involucra el uso de dirección de email y contraseña. ¿Qué si quieres habilitar a los usuarios para que inicien sesión con su nombre de usuario único? Si eso es lo que quieres, es posible. Veamos como.
Ejecuta el comando:
rails generate migration AddUsernameToUSers username:string
Esto agregará una nueva columna para username en tu tabla users. Migra tu base de datos.
rake db:migrate
Necesitas agregar un campo a tus vistas en donde tus usuarios puedan ingresar su nombre de usuario. Cuando vas a tu directorio app/views, no encontrarás ningún archivo que genere las vistas Devise. Esto es porque Devise carga las vistas desde su gemset. Para personalizarlo, tienes que generar copias de las vistas. El comando de abajo hace la magia.
rails generate devise:views
Esto generará algunas carpetas y archivos en tu directorio app/views.
Necesitarás editar la página para iniciar sesión, registrar y actualizar la información de usuario. Solo pega los bloques de código de abajo en sus respectivos archivos.
Sign-Up
1 |
#app/views/devise/registrations/new.html.erb |
2 |
|
3 |
<h2>Sign up</h2> |
4 |
|
5 |
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> |
6 |
<%= devise_error_messages! %> |
7 |
|
8 |
<div class="form-group"> |
9 |
<%= f.label :email %><br /> |
10 |
<%= f.email_field :email, autofocus: true, class: "form-control" %> |
11 |
</div>
|
12 |
|
13 |
<div class="form-group"> |
14 |
<%= f.label :username %> |
15 |
<%= f.text_field :username, class: "form-control" %> |
16 |
</div>
|
17 |
|
18 |
<div class="form-group"> |
19 |
<%= f.label :password %> |
20 |
<% if @minimum_password_length %> |
21 |
<em>(<%= @minimum_password_length %> characters minimum)</em> |
22 |
<% end %><br /> |
23 |
<%= f.password_field :password, autocomplete: "off", class: "form-control" %> |
24 |
</div>
|
25 |
|
26 |
<div class="form-group"> |
27 |
<%= f.label :password_confirmation %><br /> |
28 |
<%= f.password_field :password_confirmation, autocomplete: "off", class: "form-control" %> |
29 |
</div>
|
30 |
|
31 |
<div class="actions"> |
32 |
<%= f.submit "Sign up", class: "btn btn-primary" %> |
33 |
</div>
|
34 |
<% end %> |
35 |
|
36 |
<%= render "devise/shared/links" %> |
Edit
1 |
#app/views/devise/registrations/edit.html.erb |
2 |
|
3 |
<h2>Edit <%= resource_name.to_s.humanize %></h2> |
4 |
|
5 |
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> |
6 |
<%= devise_error_messages! %> |
7 |
|
8 |
<div class="form-group"> |
9 |
<%= f.label :email %><br /> |
10 |
<%= f.email_field :email, autofocus: true, class: "form-control" %> |
11 |
</div>
|
12 |
|
13 |
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> |
14 |
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div> |
15 |
<% end %> |
16 |
|
17 |
<div class="form-group"> |
18 |
<%= f.label :username %> |
19 |
<%= f.text_field :username, class: "form-control" %> |
20 |
</div>
|
21 |
|
22 |
<div class="form-group"> |
23 |
<%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br /> |
24 |
<%= f.password_field :password, autocomplete: "off", class: "form-control" %> |
25 |
</div>
|
26 |
|
27 |
<div class="form-group"> |
28 |
<%= f.label :password_confirmation %><br /> |
29 |
<%= f.password_field :password_confirmation, autocomplete: "off", class: "form-control" %> |
30 |
</div>
|
31 |
|
32 |
<div class="form-group"> |
33 |
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br /> |
34 |
<%= f.password_field :current_password, autocomplete: "off", class: "form-control" %> |
35 |
</div>
|
36 |
|
37 |
<div class="actions"> |
38 |
<%= f.submit "Update", class: "btn btn-primary" %> |
39 |
</div>
|
40 |
<% end %> |
41 |
|
42 |
<h3>Cancel my account</h3> |
43 |
|
44 |
<p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p> |
45 |
|
46 |
<%= link_to "Back", :back %> |
Sign-In
1 |
#app/views/devise/sessions/new.html.erb |
2 |
|
3 |
<h2>Log in</h2> |
4 |
|
5 |
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> |
6 |
<div class="form-group"> |
7 |
<%= f.label :username %><br /> |
8 |
<%= f.text_field :username, autofocus: true, class: "form-control" %> |
9 |
</div>
|
10 |
|
11 |
<div class="form-group"> |
12 |
<%= f.label :password %><br /> |
13 |
<%= f.password_field :password, autocomplete: "off", class: "form-control" %> |
14 |
</div>
|
15 |
|
16 |
<% if devise_mapping.rememberable? -%> |
17 |
<div class="form-group"> |
18 |
<%= f.check_box :remember_me %> |
19 |
<%= f.label :remember_me %> |
20 |
</div>
|
21 |
<% end -%> |
22 |
|
23 |
<div class="actions"> |
24 |
<%= f.submit "Log in", class: "btn btn-primary" %> |
25 |
</div>
|
26 |
<% end %> |
27 |
|
28 |
<%= render "devise/shared/links" %> |
Usando tu editor de texto, navega a app/controllers/application_controller.rb. Necesitas modificarlo para permitir el uso de nombre de usuario. Modifícalo para que luzca así:
1 |
#app/controllers/application_controller.rb |
2 |
|
3 |
class ApplicationController < ActionController::Base |
4 |
# Prevent CSRF attacks by raising an exception. |
5 |
# For APIs, you may want to use :null_session instead. |
6 |
protect_from_forgery with: :exception |
7 |
|
8 |
before_action :configure_permitted_parameters, if: :devise_controller? |
9 |
|
10 |
protected
|
11 |
|
12 |
def configure_permitted_parameters |
13 |
added_attrs = [:username, :email, :password, :password_confirmation, :remember_me] |
14 |
devise_parameter_sanitizer.permit :sign_up, keys: added_attrs |
15 |
devise_parameter_sanitizer.permit :account_update, keys: added_attrs |
16 |
end
|
17 |
end
|
Ahora un usuario puede iniciar sesión con su nombre de usuario. En este punto hay algo que no está bien en tu aplicación. Cuando un usuario inicia sesión, no hay manera de cerrar sesión. Esto no resulta en una experiencia de usuario grandiosa. Te mostraré como arreglar eso.
Desde tu terminal, crea un nuevo directorio llamado shared en tu folder app/views.
1 |
mkdir app/views/shared
|
2 |
touch app/views/shared/_navigation.html.erb
|
EL archivo que creaste arriba es un parcial en donde el código para tu barra de navegación será escrito. Pon el siguiente código.
1 |
#app/views/shared/_navigation.html.erb |
2 |
|
3 |
<nav class="navbar navbar-inverse"> |
4 |
<div class="container"> |
5 |
<div class="navbar-header"> |
6 |
<%= link_to 'Tutsplus Devise', root_path, class: 'navbar-brand' %> |
7 |
</div>
|
8 |
<div id="navbar"> |
9 |
<ul class="nav navbar-nav"> |
10 |
<li><%= link_to 'Home', root_path %></li> |
11 |
</ul>
|
12 |
<ul class="nav navbar-nav pull-right"> |
13 |
<% if user_signed_in? %> |
14 |
<li class="dropdown"> |
15 |
<a class="dropdown-toggle" data-toggle="dropdown" href="#"> |
16 |
<%= current_user.name %> |
17 |
<span class="caret"></span> |
18 |
</a>
|
19 |
<ul class="dropdown-menu" role="menu"> |
20 |
<li><%= link_to 'Profile', edit_user_registration_path %></li> |
21 |
<li><%= link_to 'Log out', destroy_user_session_path, method: :delete %></li> |
22 |
</ul>
|
23 |
</li>
|
24 |
<% else %> |
25 |
<li><%= link_to 'Log In', new_user_session_path %></li> |
26 |
<li><%= link_to 'Sign Up', new_user_registration_path %></li> |
27 |
<% end %> |
28 |
</ul>
|
29 |
</div>
|
30 |
</div>
|
31 |
</nav>
|
Ahora necesitas generar la barra e navegación en tu diseño de aplicación. Abre app/views/layouts/application.html.erb y pon el código para generar tu barra de navegación.
1 |
#app/views/layouts/application.html.erb |
2 |
|
3 |
... |
4 |
<div class="container-fluid"> |
5 |
<%= render "shared/navigation" %> |
6 |
|
7 |
<p class="notice"><%= notice %></p> |
8 |
<p class="alert"><%= alert %></p> |
9 |
</div>
|
10 |
... |
Conclusión
En esta parte aprendiste cómo instalar Devise y agregar autenticación a tus páginas. También hice mención de un parcial. Cubriré eso en un tutorial por separado.
En la siguiente parte, cubriré algunas áreas más avanzadas que esta. ¡Espero que haya valido tu tiempo!



