Las cuentas de usuario son una característica indispensable de las aplicaciones web. Nos permiten crear experiencias personalizadas y persistentes para las personas que utilizan nuestro sitio. También nos ayudan a realizar un mejor seguimiento de cómo se utiliza nuestra aplicación. A pesar de su utilidad, implementar cuentas de usuario puede ser como abrir la caja de problemas de Pandora.
Vivimos en una era de saqueadores inteligentes de dominios digitales. Si bien hemos abierto una caja de posibilidades, ¡han traído consigo una gran cantidad de problemas! ¿Cómo tenemos una oportunidad? Nos apoyamos en el trabajo de otros y en la configuración comercial por convención. Pero antes de encontrar algunos hombros en los que apoyarnos, tengamos una idea del problema que estamos tratando de resolver.
Registrarse y Iniciar sesión son acciones con las que todos estamos familiarizados. Permiten que las aplicaciones web autentiquen y autoricen a sus usuarios.
Cuando nos registramos , creamos un medio por el cual una aplicación web determina que somos quienes decimos que somos. El registro ocurre una vez en la vida de un usuario en una aplicación. Crea una nueva instancia de usuario en el backend del servidor con información que se puede usar para autenticar a ese usuario. La autenticación hace la pregunta: "¿Eres quien dices ser?" Cuando iniciamos sesión, somos autenticados por la aplicación en función del nombre de usuario y la contraseña que proporcionamos y, en consecuencia, tenemos acceso a diferentes funciones de la aplicación.
Estamos comprometidos con la autenticación de un solo factor cuando solo tenemos que proporcionar un tipo de información para autenticarnos. Como puede imaginar, la autenticación de un solo factor no es el mejor medio para proteger las cuentas de usuario, ya que cualquier persona con un nombre de usuario y una contraseña puede acceder a una cuenta.
Ahí es donde entra en juego la autenticación multifactor . Nos encontramos con la autenticación multifactor bastante bien todos los días, cuando un sitio nos pide que ingresemos un código generado aleatoriamente que se envía a nuestros teléfonos además de nuestro nombre de usuario y contraseña, o cuando un cajero automático requiere que ingresemos un número PIN e insertemos nuestro débito. tarjeta. No construiremos la autenticación multifactor en esta publicación, pero es importante tenerlo en cuenta.
Una vez que iniciamos sesión y nos autenticamos mediante una aplicación, verificará si estamos autorizados para realizar las acciones que intentamos realizar. La autorización hace la pregunta: "¿Tienes permiso para hacer eso?" El medio por el cual una aplicación web autoriza a un usuario es a través de cookies, en concreto, cookies de sesión.
Una cookie es un bit de datos almacenados en el navegador de un usuario. Las aplicaciones web pueden almacenar todo tipo de información en cookies para rastrear el estado y el historial de un usuario. Una cookie de sesión es una cookie que dura la duración de una sesión en una aplicación web, generalmente desde el inicio de sesión/inicio de sesión hasta el inicio de sesión/cierre de sesión.
Cuando un usuario inicia sesión, el navegador del usuario recibe una cookie de la aplicación web que identifica a ese usuario. La autorización del usuario se puede verificar cuando se realizan solicitudes HTTP de la aplicación.
El registro ocurre una vez en la vida del usuario en una aplicación web. Le da a la aplicación un medio para autenticar al usuario, o verificar que sea quien dice ser, a través de un nombre de usuario, contraseña y cualquier otra información requerida. Cuando un usuario inicia sesión, se autentica y recibe una cookie de sesión de identificación. A medida que el usuario interactúa con la aplicación, la aplicación puede verificar si el usuario está autorizado o autorizado para realizar una acción al hacer referencia a la cookie del usuario.
La autenticación es la forma que tiene una aplicación web de comprobar que un usuario es quien dice ser. Hay varias gemas de Ruby ya escritas para facilitar este proceso. Devise es uno de los más populares, junto con Omniauth y Doorkeeper .
¡No vamos a usar ninguno de esos! Con el fin de comprender el proceso de autenticación y autorización, usaremos una gema que lo ha estado observando desde que abrió por primera vez un Rails Gemfile: bcrypt .
¡Descomenta a ese tipo!
source 'https://rubygems.org' git_source( :github ) { |repo| "https://github.com/ #{repo} .git" } # ruby '2.5.1' ... # Use Redis adapter to run Action Cable in production # gem 'redis', '~> 4.0' # Use ActiveModel has_secure_password gem 'bcrypt' , '~> 3.1.7' # Use ActiveStorage variant # gem 'mini_magick', '~> 4.8' ...
bcrypt
la gema de Ruby se basa en bcrypt, el algoritmo hash de OpenBSD. Dada cualquier cadena, como una contraseña, el hash codificará la cadena junto con una pizca de caracteres aleatorios (conocidos como sal) de tal manera que el proceso no se pueda revertir o adivinar. bcrypt
la gema de Ruby nos permite utilizar el algoritmo bcrypt para hash de contraseñas, y también gestiona el almacenamiento de la sal utilizada en el hashing para que luego podamos autenticar a nuestros usuarios. pw = BCrypt::Password.create( 'P4ssW02d!' ) # => "$2a$12$YNW0EG8VwLmy1l9yxMeyAOaen/Yhx7LTBJR6G7jnG2WMkr9fo7aO6" pw == 'P4ssW02d!' # => true
Para usar
bcrypt
, nuestra tabla de usuario debe tener un password_digest
atributo. En sus proyectos de Rails, puede ejecutar lo siguiente: rails generate resource User username password_digest
.Después de migrar la tabla, deberíamos tener un esquema similar al siguiente:
create_table "users" , force: :cascade do |t| t.string "username" t.string "password_digest" t.datetime "created_at" , null: false t.datetime "updated_at" , null: false end
Tenga en cuenta que estamos almacenando un resumen de contraseña, no una contraseña. Nunca, nunca, por el motivo que sea, aunque tu madre también te lo pida, aunque creas que te curará los hongos en los pies, nunca guardes las contraseñas en texto sin formato.
También debemos agregar
has_secure_password
a nuestro modelo de usuario. Notará que he agregado algunas validaciones para garantizar que nuestros usuarios se creen con un nombre de usuario único y una contraseña no única. No requiera contraseñas únicas, ya que les dará a los ineptos la información y la confianza que necesitan para obtener acceso a las cuentas de los usuarios. class User < ApplicationRecord has_secure_password validates :username , presence: true , uniqueness: true validates :password , presence: true end
has_secure_password
es un método auxiliar para modelos que les da acceso a métodos que ayudarán en la autenticación. Podemos probarlos en rails console
. usr = User.create( username: "cheetochester" , password: "Ch33zy$" , password_confirmation: "Ch33zy$" ) # => #<User id: 4, username: "cheetochester", password_digest: "$2a$12$Io.kXzHPXoGZYzuWBawSFOawjvGNmsrHsCiXbNhlYep..." ...> usr.authenticate( "cheesys" ) # => false usr.authenticate( "Ch33zy$" ) # => #<User id: 4, username: "cheetochester", password_digest: "$2a$12$Io.kXzHPXoGZYzuWBawSFOawjvGNmsrHsCiXbNhlYep..." ...>
Tenga en cuenta que cuando creamos un nuevo usuario, lo hacemos con el
password
y password_confirmation
llaves, no password_digest
. bcrypt
maneja la validación de la contraseña y password_confirmation
y convertir la contraseña en el password_digest
que se guarda en la base de datos. Dada una cadena de contraseña, el #authenticate
El método devuelve falso si la contraseña es incorrecta y la instancia de usuario si la contraseña es correcta.Antes de que podamos configurar con confianza nuestros controladores, debemos tener una visión clara de nuestras rutas. En esta aplicación de ejemplo básica, los usuarios pueden crear y ver sus cuentas. También pueden iniciar y cerrar sesión en sus cuentas. Manejaremos esta funcionalidad con las siguientes rutas en
rails-app/config/routes.rb
: Rails.application.routes.draw do resources :users , only: [ :create , :show ] get "/signup" , to: "users#new" get "/login" , to: "sessions#new" post "/sessions" , to: "sessions#create" delete "/sessions" , to: "sessions#destroy" # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end
El extracto de código a continuación muestra un controlador de usuario básico. Hemos admitido acciones para crear un usuario y ver su página.
class UsersController < ApplicationController def new @user = User.new end def create @user = User.create(user_params) if @user.valid? @user.save redirect_to @user else redirect :new end end def show @user = User.find(params[ :id ]) end private def user_params params. require ( :user ).permit( :username , :password , :password_confirmation ) end end
No hay salsa especial para crear un formulario de registro. Simplemente necesitamos un formulario que pase
username
, password
, y password_confirmation
a nuestra aplicación. En rails-app/app/views/users/new.html.erb
podemos generar un formulario con ayudantes de formulario de Rails así: <%= form_for @user do |f| %> <%= f.label :username %> <%= f.text_field :username , placeholder: "Username" %> <%= f.label :password %> <%= f.password_field :password , placeholder: "Password" %> <%= f.label :password_confirmation %> <%= f.password_field :password_confirmation , placeholder: "Confirm Password" %> <%= f.submit "Create Account" %> <% end %>
En la presentación, nuestro
UsersController#create
action intentará crear una nueva instancia de usuario con esos parámetros. Si la instancia de usuario es válida, el usuario será redirigido a su página de presentación. De lo contrario, el usuario será redirigido a la página de nuevo usuario.El inicio de sesión o el inicio de sesión se gestionará a través de Rails.
session
hash, que lee y gestiona un session
cookie en el navegador de un usuario. Administrar la sesión requiere suficientes acciones únicas para justificar otro controlador. Llamaremos a este controlador el SessionsController
. class SessionsController < ApplicationController def new end def create @user = User.find_by( username: params[ :username ]) if @user && @user.authenticate(params[ :password ]) session[ :user_id ] = @user.id redirect_to @user else redirect_to login_path end end end
En este controlador hemos definido dos acciones.
SessionsController#new
es simplemente responsable de mostrar una página que permite a un usuario crear una nueva sesión (también conocida como inicio de sesión).Los contenidos de
rails-app/app/views/sessions/new.html.erb
tal vez tan simple como: <%= form_tag sessions_path do %> <%= label_tag "Username" %> <%= text_field_tag :username , nil , placeholder: "Username" %> <%= label_tag "Password" %> <%= password_field_tag :password , nil , placeholder: "Password" %> <%= submit_tag "Log In" %> <% end %>
El propósito de este formulario es solicitar la autenticación de un usuario y el almacenamiento de su identidad en el
session
Galleta.En
SessionsController#create
, primero intentamos encontrar al usuario en función de su nombre de usuario. Si el usuario existe, lo autenticamos mediante la contraseña facilitada en el formulario y el #authenticate
método que nos otorga el bcrypt
joya. Si están autenticados, almacenaremos su identificación en la cookie de sesión ( session[:user_id] = @user.id
).De lo contrario, serán redirigidos a la ruta de inicio de sesión.
¿Qué evita que alguien simplemente modifique su cookie de sesión para imitar a un usuario? ¡Por suerte para nosotros, Rails encripta las cookies que crea!
Implementamos la autenticación con un par de formularios, un puñado de rutas y acciones de controlador, y algunas
bcrypt
métodos. Y sin embargo, un pájaro de un solo ala no puede volar. Necesitamos autorización para poner a funcionar nuestra autenticación.Podemos sentar las bases para la autorización escribiendo métodos auxiliares en
rails-app/app/controllers/application_controller.rb
. class ApplicationController < ActionController::Base helper_method :logged_in? , :current_user def current_user if session[ :user_id ] @user = User.find(session[ :user_id ]) end end def logged_in? !!current_user end def authorized redirect_to login_path unless logged_in? end end
Aquí hemos definido algunos métodos auxiliares para usar en nuestros controladores y vistas.
ApplicationController#current_user
busca un valor en el :user_id
clave de la session
hachís de galletas. Si hay uno, se devolverá la instancia de usuario correspondiente.De lo contrario, el valor de retorno es
nil
. #logged_in?
coacciona el valor de retorno de current_user
en un valor booleano mediante el uso de un doble golpe. #authorized
activará una redirección a la página de inicio de sesión a menos que un usuario haya iniciado sesión (es decir, a menos que un valor de session[:user_id]
existe y coincide con el valor de una identificación de un usuario existente)los
before_action
El método de devolución de llamada puede ayudarnos a simplificar el uso de nuestros métodos de autorización. En nuestro UsersController
, agregaremos: class UsersController < ApplicationController before_action :authorized , only: [ :show ] ... end
Con esta línea, la aplicación verificará si un usuario ha iniciado sesión y lo redirigirá a la ruta de inicio de sesión si no lo está antes de la
#show
incluso se dispara la acción. Pero la diversión no se detiene aquí. ¡Podemos aprovechar nuestros métodos auxiliares de autorización para representar condicionalmente nuestras vistas!En
rails-app/app/views/layouts/application.html.erb
, agreguemos un botón de inicio/cierre de sesión debajo de nuestra plantilla de vista. De esa manera, aparecerá en cualquier página que agreguemos a nuestra aplicación.Recordar que
<%= yield %>
en application.html.erb
es donde se representan las plantillas correspondientes a las acciones de nuestro controlador. <!DOCTYPE html> <html> ... <body> <%= yield %> <br> <% if logged_in? %> <%= button_to "Logout" , sessions_path, method: :delete %> <% else %> <%= button_to "Login Page" , login_path, method: :get %> <% end %> < /body> </html >
Ya que definimos el
#logged_in?
método auxiliar y aprobado :logged_in?
como argumento para helper_method
en nuestro ApplicationController
, podemos usarlo en nuestras vistas. este poco de erb
muestra un botón de cierre de sesión si un usuario ha iniciado sesión y viceversa.Podemos ser aún más específicos. Ya que tenemos un método auxiliar
current_user
que devuelve el usuario de la sesión actual, podemos generar contenido personalizado para ese usuario.Sobre el
rails-app/app/views/users/show.html.erb
página, podemos hacer que un usuario vea contenido especial si está en su propia página de presentación. <h1>Welcome to the page of <%= @user.username %>< /h2> <% if current_user == @user %> <h3>This is my page!!!</h 3> <% end %
Toda buena historia tiene que llegar a su fin. Cuando un usuario cierra la sesión, esencialmente está finalizando su
session
. Una forma básica de finalizar la sesión de un usuario es establecer el valor de session[:user_id]
a nil
. Agregaremos la siguiente acción a nuestro SessionsController
para hacerlo class SessionsController < ApplicationController ... def destroy session[ :user_id ] = nil redirect_to login_path end end
Si es del tipo observador, habrá notado que el botón de cierre de sesión que creamos en la última sección realiza la solicitud HTTP que se enrutará a esta acción. Con esa acción, la cookie de sesión dejará de saber sobre el usuario y el usuario no estará autorizado en la aplicación.
Y eso es todo lo que se necesita para desarrollar la autenticación y autorización básicas en una aplicación Rails con
bcrypt
! Cuando autenticamos a un usuario, verificamos que sea quien dice ser. Facilitamos la autenticación en una aplicación web a través de formularios de registro e inicio de sesión. Para hacer uso de la autenticación, rastreamos el estado del usuario en una cookie de sesión y usamos esa cookie para realizar la autorización antes de las acciones relevantes en nuestra aplicación. bcrypt
nos ayuda a implementar la autenticación brindándonos un medio para almacenar de forma segura y hacer referencias cruzadas de factores de autenticación (como contraseñas) en nuestra base de datos. Para autorizar a los usuarios, escribimos métodos de ayuda que utilizan algunos de esos medios, así como la cookie de sesiones de Rails integrada para verificar el estado del usuario y reaccionar en consecuencia. Ahora que tiene una idea de cómo funciona, intente implementarlo usted mismo. Cuando te sientas cómodo usando
bcrypt
, romper las armas grandes como devise
e intenta de nuevo. La autenticación y la autorización son partes esenciales de las aplicaciones web seguras.Todos estaremos mejor si los usas. Y nunca almacene contraseñas de texto sin formato en una base de datos.