4 January 2014

Аутентификация в Rails-приложениях с помощью Devise. Часть 1: базовая настройка

RubyRuby on Rails
Sandbox
Предлагаю вашему вниманию один из лучших, на мой взгляд, гемов для аутентификации в rails-приложениях. К сожалению, русскоязычной информации по данному гему очень мало, в том числе и на хабре, поэтому хочу осветить данную тему. Статья, в первую очередь, рассчитана на новичков и представляет из себя подробный туториал по настройке аутентификации на основе упомянутого гема. В первой части я освещу шаги по базовой настройке. Далее подробнее о геме.

Devise — это ruby-гем, предоставляющий возможности для аутентификации в rails-приложениях. Devise работает в связке с гемом Warden, который в свою очередь предоставляет сам механизм для аутентификации в rack-базированных ruby-приложениях. Основные особенности Devise описаны ниже:
  • основан на Rack;
  • является законченным MVC-решением, основанным на Rails;
  • разрешает вход в систему по нескольким моделям одновременно;
  • основан на модульности: использует только то, что вам действительно необходимо.

Итак, приступим к установке и настройке Devise для вашего rails-приложения. Дальнейший процесс для удобочитаемости будет разбит на отдельные шаги.

Примечание: вся установка и настройка мною производилась для rails версии 4.0.1.

Шаг 1. Добавим гем в Gemfile
gem 'devise'

Либо с указанием точной версии (следующая версия у меня стабильно работает с rails 4.0.1)
gem 'devise', '3.2.2'

Запустим bundle для установки новых гемов
bundle install

В качестве зависимостей будут установлены дополнительно следующие гемы:
warden — связующее ПО, предоставляющее возможность аутентификации для Rack-приложений;
orm_adapter — предоставляет единую точку входа для использования основных функций Ruby ORMs;
bcrypt-ruby — предоставляет простую обертку для работы с паролями. В основе лежит криптографическая хэш-функция bcrypt();
thread_safe — предоставляет потоко-безопасные коллекции и утилиты для Ruby;
railties — внутренние компоненты Rails, такие как загрузчики приложения, плагины, генераторы и rake-задачи.

Шаг 2. Итак, гем был скачан, но еще никак не взаимодействует с нашим приложением. Devise имеет в своем арсенале удобные генераторы, одним из которых мы сейчас и воспользуемся. Выполним установку Devise путем запуска следующего генератора:
rails generate devise:install

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

Шаг 3. Произведем базовую настройку гема после установки
3.1. Сейчас нам необходимо задать настройки мейлера (отправщика почты) для каждой из сред выполнения. Для среды разработки необходимо добавить следующую строку в файл config/environments/development.rb:
config.action_mailer.default_url_options = { :host => 'localhost:3000' }

Для продакшн-среды необходимо заменить значение ключа :host на актуальное.

3.2. После входа пользователя в систему, подтверждения аккаунта или обновления пароля, Devise будет искать путь для последующего перенаправления. По умолчанию он выполнит перенаправление к user_root_path, если тот существует. В противном случае Devise выполнит перенаправление к root_path. Поэтому этот путь должен быть обязательно определен в приложении. Проверьте свой файл роутов config/routes.rb на наличие строки следующего вида:
root 'home#index'

Думаю, вы со мной согласитесь, что не очень логично после входа перенаправлять пользователя на главную страницу приложения. В связи с этим, давайте настроим перенаправление после успешного входа на страницу профиля пользователя. Для этого добавим следующий маршрут в config/routes.rb:
get 'persons/profile', as: 'user_root'

Примечание:
1) Предварительно необходимо создать контроллер и экшен. Это можно выполнить через консоль с помощью удобного генератора:
rails generate controller persons profile

2) В итоге, генератор создаст контроллер persons_controller с одним методом (экшеном) profile, а также вид для этого экшена.
Список роутов можно получить через консоль. Достаточно ввести команду:
bundle exec rake routes

В нашем случае должен быть доступен следующий роутер
user_root GET    /persons/profile(.:format)  persons#profile

для которого будет доступен хэлпер user_root_path

Существует также и другой путь, с помощью которого можно задать собственные перенаправления как после входа, так и после выхода. Для этого необходимо переопределить в ApplicationController (app/controllers/application_controller.rb) существующие devise-методы after_sign_in_path_for и after_sign_out_path_for. Лично мне, больше понравился этот вариант настройки перенаправлений:
def after_sign_in_path_for(resource)
  current_user_path
end

После входа пользователь будет перенаправлен на страницу, описанную хэлпером current_user_path
def after_sign_out_path_for(resource_or_scope)
  request.referrer
end

После выхода пользователь останется на той же странице.

3.3. Необходимо в шаблон добавить вывод уведомлений и предупреждений
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>

3.4. Если вы разворачиваете приложение на Heroku с использованием Rails версии 3.2, то нужно добавить следующую строку в config/application.rb
config.assets.initialize_on_precompile = false

3.5. Мы можем настроить файлы видов под свои нужды. Для этого необходимо их скопировать из гема в свое приложения путем запуска следующей команды:
rails generate devise:views

В директории app/views/devise вы найдете все используемые гемом файлы видов. Можете настроить их по своему желанию, под общий стиль вашего приложения.

Шаг 4. Теперь подошло время создать модель пользователя, которая будет выполнять аутентификацию. Мы будем генерировать модель пользователя, поэтому назовем ее User. Модель может также называться Admin:
rails generate devise User

Данный генератор создаст новую модель, если ее не существовало ранее и сконфигурирует ее с учетом используемых по умолчанию модулей. Скорее всего, вы получите следующий код в app/models/user.rb (код несколько отформатирован):
class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable,
         :registerable,
         :recoverable,
         :rememberable,
         :trackable,
         :validatable

end

Devise имеет в своем арсенале 10 модулей. По умолчанию у вас будет подключено 6 модулей. Вы можете отредактировать этот список. Описание всех доступных модулей привожу ниже:

1. Database Authenticatable: предоставляет возможность входа в систему на основе зашифрованного и хранимого в базе данных пароля. Вход может быть выполнен посредством отправки POST-запроса или с помощью HTTP Basic Authentication.
2. Omniauthable: добавляет поддержку Omniauth (https://github.com/intridea/omniauth).
3. Confirmable: позволяет отправлять письмо с инструкциями для подтверждения аккаунта, созданного во время регистрации.
4. Recoverable: позволяет восстанавливать забытый пароль. Отправляет инструкции по восстановлению на почту.
5. Registerable: управляет регистрацией пользователей, позволяет редактировать и удалять аккаунты.
6. Rememberable: позволяет запоминать пользователей на основе cookies. Управляет созданием и удалением токенов.
7. Trackable: ведет статистику количества входов, учитывает время и IT-адреса.
8. Timeoutable: отвечает за продолжительность сессии активности пользователя в системе;
9. Validatable: предоставляет инструменты валидации e-mail и пароля. Модуль может быть легко настроен, вы можете определить собственные валидаторы.
10. Lockable: блокирует аккаунт после указанного в настройках количество неудачных попыток авторизации. Аккаунт может быть разблокирован посредством email или через определенный период времени.

Также команда, которую мы выполнили выше, создаст файл миграции БД и роут. Файл миграции выглядит следующим образом:
class DeviseCreateUsers < ActiveRecord::Migration
  def change
    create_table(:users) do |t|
      ## Database authenticatable
      t.string :email,              :null => false, :default => ''
      t.string :encrypted_password, :null => false, :default => ''

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, :default => 0, :null => false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, :default => 0, :null => false # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at


      t.timestamps
    end

    add_index :users, :email,                :unique => true
    add_index :users, :reset_password_token, :unique => true
    # add_index :users, :confirmation_token,   :unique => true
    # add_index :users, :unlock_token,         :unique => true
  end
end

Здесь мы видим, что создается таблица пользователей (3-я строка кода). Так как мы назвали нашу модель User, то таблица имеет такое же название, но во множественном числе (согласно соглашению по именованию ActiveRecord). Далее последовательно описываются поля, которые будут добавлены в таблицу при создании. Как мы видим из кода, только 6 модулям Devise для работы могут потребоваться поля в таблице.

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

В файле config/routes.rb будет создан следующий маршрут:
devise_for :users

Напомню, что список существующих маршрутов можно посмотреть по команде:
bundle exec rake routes


Шаг 5. Итак, с модулями мы разобрались, необходимые подключили, просмотрели файл миграции. Теперь необходимо выполнить миграцию, которая создаст таблицу пользователей с необходимым набором полей. Выполним следующую команду:
bundle exec rake db:migrate


Шаг 6. Далее обязательно нужно перезапустить приложение, чтобы все изменения вступили в силу. Если этого не сделать, то можно столкнуться с различного рода ошибками.
Так как мое приложение работает под управлением сервера Unicorn, то я перезапускаю именно его. Если вы тестируете свое приложение под сервером Webrick, то достаточно будет остановить его с помощью комбинации клавиш CTRL+C, а затем запустить заново:
rails server


Шаг 7. Наш модуль и наше приложение готовы к работе. Следующим шагом будет добавление в шаблон ссылок для входа и регистрации. Это можно сделать следующим образом:
<% if user_signed_in? %>
    <span>Здравствуйте, <%= current_user.email %></span>
    <%= link_to 'Выйти', destroy_user_session_path, :method => :delete %>
<% else %>
    <%= link_to 'Войти', new_user_session_path %> или <%= link_to 'Зарегистрироваться', new_user_registration_path %>
<% end %>

Если пользователь вошел на сайт, то пишем ему «Здравствуйте, [почта пользователя]» и рядом размещаем ссылку для выхода. Если пользователь еще не вошел на сайт, то располагаем рядом две ссылки «Войти» и «Зарегистрироваться». В качестве путей для ссылок используем существующие хелперы. Напомню, что маршруты были установлены в четвертом шаге и им список можно посмотреть по следующей команде. Хелпер состоит из префикса (самая левая колонка + '_path')
bundle exec rake routes


Шаг 8. Самое время начать использовать Devise по назначению — для ограничения доступа неавторизованному пользователю к определенным разделам сайта. Допустим, мы хотим разрешить выполнять заказ лишь авторизованному пользователю. Для этого в соответствующий контроллер мы должны добавить фильтр:
class OrdersController < ApplicationController
  before_filter :authenticate_user!, except => [:show, :index]

  def index
    # do something
  end

  def show
    # do something
  end

  def create
    # do something
  end
end

Здесь, в коде, вторым параметром в метод before_filter мы передает список методов контроллера, которые не должны фильтроваться. Таким образом, авторизация необходима только лишь для выполнения заказа (доступа к работе метода order). Если необходимо фильтровать все методы контроллера, то второй параметр передавать не нужно. Достаточно лишь написать:
before_filter :authenticate_user!


Список полезных методов-хэлперов

Devise содержит полезные хэлперы, которые можно использовать внутри контроллеров и видов. Некоторые из них привожу ниже.
Проверить факт входа пользователя в систему можно с помощью следующего хэлпера:
user_signed_in?

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

Вывести электронную почту пользователя можно следующим образом
current_user.email

Доступ к текущей сессии можно получить так:
user_session


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

Список используемой литературы:

В качестве основного источника материала использовалась официальная документация по гему — github.com/plataformatec/devise, дополненная собственным опытом настройки.
Tags:deviseаутентификация пользователей
Hubs: Ruby Ruby on Rails
+15
72.9k 155
Comments 19
Программист Ruby on Rails
from 100,000 ₽TIQUMRemote job
Бэкенд-разработчик (Ruby on Rails)
from 120,000 ₽FunBoxМоскваRemote job
Senior Backend-разработчик (Ruby on Rails)
from 200,000 to 325,000 ₽HoodiesRemote job
Ruby on Rails Developer
from 90,000 to 180,000 ₽FlatstackRemote job
Программист (Ruby on Rails, React)
from 1,500 to 3,000 $HexletRemote job