Статьи

Аутентификация Rails с помощью Authlogic

Сегодня я хотел бы познакомить вас с AuthLogic , простым решением для аутентификации Ruby, созданным Беном Джонсоном.

AuthLogic ненавязчив и довольно низкоуровневый. Он не опирается на сгенерированный код (например, Devise ) — он предоставляет только инструменты, которые вы можете использовать для кодирования своего приложения так, как вам нравится. В некотором смысле AuthLogic похож на Волшебство .

Давайте создадим приложение Rails, чтобы пользователи могли регистрироваться, входить и выходить, а также сбрасывать свой пароль. При этом мы обсудим различные функции и настройки AuthLogic. После прочтения этого урока вы будете готовы использовать AuthLogic в ваших реальных проектах.

Исходный код можно найти на GitHub .

Демо-приложение доступно на sitepoint-authlogic.herokuapp.com .

Начиная

Демонстрационное приложение, которое я собираюсь показать, построено с использованием Rails 4.1, но AuthLogic поддерживает Rails 3 и даже Rails 2 (для этого есть отдельная ветка ).

Создайте новое «логическое» приложение без набора тестов по умолчанию:

$ rails new Logical -T 

Оставьте следующие драгоценные камни:

Gemfile

 [...] gem 'bootstrap-sass' gem 'authlogic', '3.4.6' [...] 

и беги

 $ bundle install 

Воспользуйтесь стилями Bootstrap, если хотите:

таблицы стилей / application.scss

 @import 'bootstrap-sprockets'; @import 'bootstrap'; 

и измените макет:

просмотров / макеты / application.html.erb

 [...] <nav class="navbar navbar-inverse"> <div class="container"> <div class="navbar-header"> <%= link_to 'Logical', root_path, class: 'navbar-brand' %> </div> <div id="navbar"> <ul class="nav navbar-nav"> <li><%= link_to 'Home', root_path %></li> </ul> </div> </div> </nav> <div class="container"> <% flash.each do |key, value| %> <div class="alert alert-<%= key %>"> <%= value %> </div> <% end %> <%= yield %> </div> [...] 

Теперь создайте контроллер для статических страниц:

pages_controller.rb

 class PagesController < ApplicationController def index end end 

Тогда соответствующий вид:

просмотров / страниц / index.html.erb

 <div class="jumbotron"> <div class="container"> <h1>Welcome!</h1> <p>Sign up to get started.</p> <p> <%= link_to '#', class: 'btn btn-primary btn-lg' do %> Sign Up &raquo; <% end %> </p> </div> 

и маршрут:

конфиг / routes.rb

 [...] root to: 'pages#index' [...] 

Когда вы закончите, перейдите к следующему шагу и давайте интегрировать AuthLogic вместе!

Настройка AuthLogic

модели

Использование AuthLogic включает две модели: базовую, которую мы будем называть User и «специальную», унаследованную от Authlogic::Session::Base которая будет называться UserSession (вы можете прочитать этот раздел, чтобы быстро понять, как это решение работает).

Прежде всего, создайте новую миграцию:

 $ rails g model User email:string crypted_password:string password_salt:string persistence_token:string 

( Вот пример миграции со всеми возможными полями)

Добавьте следующую строку в сгенерированную миграцию:

Миграции / xxx_create_users.rb

 [...] add_index :users, :email, unique: true [...] 

Применить миграцию:

 $ rake db:migrate 

crypted_password email , crypted_password и password_salt самом деле являются необязательными. AuthLogic на самом деле не заботится о том, как вы проходите аутентификацию — используйте LDAP или OAuth 2, например. Вот список «дополнений» AuthLogic для поддерживаемых методов аутентификации, однако многие из них активно не поддерживаются.

Кстати, у вас также может быть поле для login которое AuthLogic будет использовать вместо email .

Поле persistence_token обязательно для заполнения. AuthLogic использует его для безопасного сохранения сеанса пользователя.

Теперь настройте модель:

модели / user.rb

 [...] acts_as_authentic [...] 

Это дает модели User функциональность AuthLogic. acts_as_authentic принимает блок для переопределения настроек по умолчанию (например, правила проверки пароля — мы обсудим это в последнем разделе статьи).

Теперь создайте новую специальную модель:

модели / user_session.rb

 class UserSession < Authlogic::Session::Base end 

Примечание для пользователей Windows

Существует довольно серьезная ошибка, с которой пользователи Windows могут столкнуться при использовании AuthLogic с Ruby 2.1 или 2.2 (я не тестировал Ruby 1.9). Полное обсуждение можно найти на GitHub , но, вкратце, эта ошибка связана с SCrypt , гемом , который реализует алгоритм безопасного хеширования паролей. AuthLogic использует SCrypt в качестве поставщика шифрования по умолчанию, но в Windows он постоянно возвращает ошибку сегментации, и сервер падает.

Самый быстрый способ — использовать другого провайдера — AuthLogic предлагает несколько из них .

В этой демонстрации я буду придерживаться SHA512, так что настройте модель:

модели / user.rb

 [...] acts_as_authentic do |c| c.crypto_provider = Authlogic::CryptoProviders::Sha512 end [...] 

Контроллеры и Помощники

Давайте позаботимся о контроллере для управления пользователями:

users_controller.rb

 class UsersController < ApplicationController def new @user = User.new end def create @user = User.new(users_params) if @user.save flash[:success] = "Account registered!" redirect_to root_path else render :new end end private def users_params params.require(:user).permit(:email, :password, :password_confirmation) end end 

Это действительно базовый контроллер, который будет отвечать за регистрацию пользователей. Обязательно разрешите оба
Атрибуты password и password_confirmation , потому что по умолчанию AuthLogic проверяет, совпадают ли эти два.

Вот контроллер для управления входом и выходом:

user_sessions_controller.rb

 class UserSessionsController < ApplicationController def new @user_session = UserSession.new end def create @user_session = UserSession.new(user_session_params) if @user_session.save flash[:success] = "Welcome back!" redirect_to root_path else render :new end end def destroy current_user_session.destroy flash[:success] = "Goodbye!" redirect_to root_path end private def user_session_params params.require(:user_session).permit(:email, :password, :remember_me) end end 

Как видите, мы используем модель UserSession для аутентификации пользователя. Он автоматически сохраняет сеанс пользователя, поэтому контроллер действительно чистый и простой.

Как насчет current_user_session ? Это вспомогательный метод:

application_controller.rb

 [...] private def current_user_session return @current_user_session if defined?(@current_user_session) @current_user_session = UserSession.find end def current_user return @current_user if defined?(@current_user) @current_user = current_user_session && current_user_session.user end helper_method :current_user_session, :current_user [...] 

UserSession.find автоматически использует persistence_token для поиска текущего сеанса.

Я также добавил current_user для простого извлечения записи текущего пользователя на основе текущего сеанса.

Маршруты

Мы должны настроить несколько маршрутов:

конфиг / routes.rb

 [...] resources :users, only: [:new, :create] resources :user_sessions, only: [:create, :destroy] delete '/sign_out', to: 'user_sessions#destroy', as: :sign_out get '/sign_in', to: 'user_sessions#new', as: :sign_in [...] 

Взгляды

Наконец, давайте разберемся с мнениями.

Прежде всего, макет:

просмотров / макеты / application.html.erb

 [...] <nav class="navbar navbar-inverse"> <div class="container"> <div class="navbar-header"> <%= link_to 'Logical', root_path, class: 'navbar-brand' %> </div> <div id="navbar"> <ul class="nav navbar-nav"> <li><%= link_to 'Home', root_path %></li> </ul> <ul class="nav navbar-nav pull-right"> <% if current_user %> <li><span><%= current_user.email %></span></li> <li><%= link_to 'Sign Out', sign_out_path, method: :delete %></li> <% else %> <li><%= link_to 'Sign In', sign_in_path %></li> <% end %> </ul> </div> </div> </nav> [...] 

Здесь я просто заполняю верхнее меню.

Теперь страница «Регистрация»:

просмотров / пользователей / new.html.erb

 <div class="page-header"><h1>Register</h1></div> <%= form_for @user do |f| %> <%= render 'shared/errors', object: @user %> <div class="form-group"> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :password_confirmation %> <%= f.password_field :password_confirmation, class: 'form-control' %> </div> <%= f.submit 'Register', class: 'btn btn-primary btn-lg' %> <% end %> 

AuthLogic автоматически проверяет, что электронное письмо является действительным и что пароли совпадают и имеют длину не менее 4 символов. Вы можете легко переопределить эти настройки внутри model / user.rb — мы обсудим это чуть позже .

Добавьте общий частичный для отображения ошибок:

просмотров / общий / _errors.html.erb

 <% if object.errors.any? %> <div class="panel panel-warning errors"> <div class="panel-heading"> <h5><i class="glyphicon glyphicon-exclamation-sign"></i> Found errors while saving</h5> </div> <ul class="panel-body"> <% object.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> 

Не забудьте добавить ссылку на страницу «Регистрация» внутри index.html.erb :

просмотров / страниц / index.html.erb

 <div class="jumbotron"> <div class="container"> <h1>Welcome!</h1> <p>Sign up to get started.</p> <p> <%= link_to new_user_path, class: 'btn btn-primary btn-lg' do %> Sign Up &raquo; <% end %> </p> </div> </div> 

И, наконец, код для представления «Вход»:

просмотров / user_sessions / new.html.erb

 <div class="page-header"><h1>Sign In</h1></div> <%= form_for @user_session do |f| %> <%= render 'shared/errors', object: @user_session %> <div class="form-group"> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :remember_me %> <%= f.check_box :remember_me %> </div> <%= f.submit "Log in!", class: 'btn btn-primary btn-lg' %> <% end %> 

На этом этапе вы готовы — загрузите сервер и зарегистрируйте своего первого пользователя!

Хранение дополнительной информации

AuthLogic поддерживает несколько «магических атрибутов», которые заполняются автоматически. Вы можете использовать их для хранения дополнительной информации о пользователе, такой как дата последнего входа или последний использованный IP.

Идите и создайте новую миграцию:

 $ rails g migration add_magic_columns_to_users 

Настроить это так:

xxx_add_magic_columns_to_users.rb

 class AddMagicColumnsToUsers < ActiveRecord::Migration def change add_column :users, :login_count, :integer, :null => false, :default => 0 add_column :users, :failed_login_count, :integer, :null => false, :default => 0 add_column :users, :last_request_at, :datetime add_column :users, :current_login_at, :datetime add_column :users, :last_login_at, :datetime add_column :users, :current_login_ip, :string add_column :users, :last_login_ip, :string end end 

и запустите миграцию:

 $ rake db:migrate 

Теперь для простоты давайте отобразим всю эту информацию на главной странице, если пользователь вошел в систему:

просмотров / страниц / index.html.erb

 <% if current_user %> <div class="page-header"><h1>Welcome back!</h1></div> <h2>Some info about you...</h2> <div class="well well-lg"> <ul> <li>Login count: <%= current_user.login_count %></li> <li>Failed login count: <%= current_user.failed_login_count %></li> <li>Last request: <%= current_user.last_request_at %></li> <li>Current login at: <%= current_user.current_login_at %></li> <li>Last login at: <%= current_user.last_login_at %></li> <li>Current login IP: <%= current_user.current_login_ip %></li> <li>Last login IP: <%= current_user.last_login_ip %></li> </ul> </div> <% else %> [...] <% end %> 

Войдите и наблюдайте за результатом. Это может пригодиться, если вы хотите проверить, например, как часто ваши пользователи посещают веб-сайт.

Сброс пароля

Пользователи, как правило, забывают свои пароли, поэтому крайне важно предоставить им способ их сброса. AuthLogic также предоставляет инструмент для добавления этой функциональности.

Автор AuthLogic предлагает использовать простой механизм, где пользователь сначала вводит электронное письмо, затем получает ссылку для обновления пароля, а затем следует по ссылке, чтобы фактически установить новый пароль. Эта ссылка содержит специальный «скоропортящийся» токен, который необходимо сбросить.

Поэтому нам нужно добавить новое поле с именем perishable_token в таблицу users . Обратите внимание, что мы не называем это чем-то вроде reset_password_token . AuthLogic не требует, чтобы этот токен мог использоваться только для сброса пароля — вы можете использовать его, например, для активации учетных записей пользователей.

Помимо perishable_token, AuthLogic также поддерживает single_access_token который идеально подходит для API — он предоставляет доступ, но не сохраняется. Узнайте больше здесь .

Итак, создайте и примените новую миграцию:

 $ rails g migration add_perishable_token_to_users perishable_token:string $ rake db:migrate 

Очевидно, нам нужен специальный контроллер для управления сбросом пароля и почтовой программой.

Начать с контроллера:

password_resets_controller.rb

 class PasswordResetsController < ApplicationController def new end def create @user = User.find_by_email(params[:email]) if @user @user.deliver_password_reset_instructions! flash[:success] = "Instructions to reset your password have been emailed to you." redirect_to root_path else flash[:warning] = "No user was found with that email address" render :new end end def edit @user = User.find_by(perishable_token: params[:id]) end def update @user = User.find_by(perishable_token: params[:id]) if @user.update_attributes(password_reset_params) flash[:success] = "Password successfully updated!" redirect_to root_path else render :edit end end private def password_reset_params params.require(:user).permit(:password, :password_confirmation) end end 

new является действие, которое будет вызываться, когда пользователь нажимает ссылку «Забыли пароль?».

create обрабатывает отправленные данные и пытается загрузить пользователя по электронной почте. Если этот пользователь найден, инструкции по сбросу пароля отправляются на его электронную почту.

edit вызывается, когда пользователь посещает ссылку, отправленную на указанный адрес электронной почты. Эта ссылка содержит наш скоропортящийся токен, который используется для поиска записи пользователя. Страница редактирования содержит другую форму для ввода нового пароля.

Внутри действия update , извлеките пользователя и обновите его пароль.

Нам также понадобятся маршруты:

конфиг / routes.rb

 ... resources :password_resets, only: [:new, :create, :edit, :update] ... 

Теперь добавьте новый метод в вашу модель:

модели / user.rb

 [...] def deliver_password_reset_instructions! reset_perishable_token! PasswordResetMailer.reset_email(self).deliver_now end [...] 

reset_perishable_token! это метод, предоставляемый AuthLogic — он просто устанавливает perishable_token в новое случайное значение и сохраняет запись.

Мы также должны создать наш новый почтовик:

отправители / application_mailer.rb

 class ApplicationMailer < ActionMailer::Base default from: "[email protected]" layout 'mailer' end 

отправители / password_reset_mailer.rb

 class PasswordResetMailer < ApplicationMailer def reset_email(user) @user = user mail(to: @user.email, subject: 'Password reset instructions') end end 

просмотров / макеты / mailer.text.erb

 <%= yield %> 

просмотров / password_reset_mailer / reset_email.text.erb

 Here is your link to reset password: <%= edit_password_reset_url(@user.perishable_token) %>. 

Также не забудьте установить параметры URL по умолчанию:

конфигурации / среда / development.rb

 config.action_mailer.default_url_options = { host: '127.0.0.1:3000' } 

Обратите внимание, что в среде разработки электронные письма фактически не будут отправляться, но вы сможете увидеть их содержимое в консоли. Также обратите внимание, что мое демонстрационное приложение не будет отправлять электронные письма, но его легко настроить для производственной среды. Узнайте больше здесь .

Наконец, обновите представления, добавив ссылку «Забыли пароль?»:

просмотров / user_sessions / new.html.erb

 <div class="page-header"><h1>Sign In</h1></div> <%= form_for @user_session do |f| %> [...] <%= f.submit "Log in!", class: 'btn btn-primary btn-lg' %> <br/><br/> <%= link_to 'Forgot your password?', new_password_reset_path, class: 'btn btn-sm btn-default' %> <% end %> 

А теперь иди и проверь, как это работает!

Настройки по умолчанию

Как я уже говорил вам, AuthLogic предоставляет некоторые настройки по умолчанию, которые вы можете легко изменить, передав блок acts_as_authentic . Чтобы узнать больше об этих настройках, вы можете просмотреть документацию , но позвольте мне выделить некоторые из них.

Грубая сила

По умолчанию AuthLogic пытается предотвратить атаки методом «грубой силы» (когда кто-то использует программу, чтобы «угадать» пароль, просто используя различные комбинации символов), но только если в вашей таблице присутствует поле failed_login_count . Есть две настройки, которые вы можете изменить:

  • consecutive_failed_logins_limit (по умолчанию 50) — допустимое количество последовательных неудачных входов в систему. Установите 0 чтобы отключить защиту от перебора.
  • failed_login_ban_for (по умолчанию 2 часа) — как долго учетная запись пользователя будет заблокирована. Установите 0 чтобы заблокировать навсегда.

Подробнее можно прочитать здесь .

HTTP Basic Auth

AuthLogic поддерживает базовую аутентификацию HTTP , которая включена по умолчанию.

Конфигурация пароля

Существуют различные настройки, которые можно использовать для изменения полей по умолчанию для хранения информации о логине и пароле, а также для изменения способа поиска сеанса пользователя.

Существует также страница, на которой перечислены параметры, связанные с правилами проверки пароля и подтверждением пароля.

Выйти на Таймаут

Вы также можете указать AuthLogic пометить пользователя как вышедшего из системы через определенный промежуток времени. Просто установите для logout_on_timeout значение true и используйте stale? чтобы проверить, нужно ли пользователю снова войти в систему.

Узнайте больше здесь .

Вы также можете использовать параметр logged_in_timeout чтобы определить, вошел ли пользователь в систему или нет. Больше можно найти здесь .

Конфигурация электронной почты

Посетите эту страницу, чтобы узнать о различных опциях, связанных с электронной почтой (поле для хранения электронной почты, правила проверки и многое другое).

Есть много других опций, которые вы можете использовать для настройки AuthLogic, поэтому обязательно просмотрите документацию.

Вывод

В этой статье мы взглянули на AuthLogic и создали простое демонстрационное приложение. Действительно интересно наблюдать за различными решениями аутентификации и сравнивать их философии, не так ли?

Какое решение для аутентификации вы предпочитаете и почему? Сталкивались ли вы с какими-либо ограничениями или особыми случаями, которые требовали от вас разработки собственной системы аутентификации? Поделитесь своим опытом!

Обратная связь приветствуется, как всегда. Спасибо, что остаетесь со мной и до скорой встречи.