Опубликован: 27.01.2016 | Уровень: для всех | Доступ: платный
Лекция 8:

Войти, выйти

Теперь, когда новые пользователи могут регистрироваться на нашем сайте (Глава 7), пришло время дать зарегистрированным пользователям возможность входить на сайт и выходить из него. Это позволит нам добавить настройки, зависящие от регистрационного статуса и личности текущего пользователя. Например, в этой главе мы добавим в header сайта ссылки войти/выйти и ссылку на профиль пользователя. В Главе 10 мы будем использовать идентификацию вошедшего в систему пользователя для создания микросообщений, связанных с этим пользователем и в Главе 11 мы позволим текущему пользователю следовать за другими пользователями приложения (тем самым получать поток (feed) их микросообщений).

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

После реализации ядра аутентификационного механизма мы немного отвлечемся от основной темы для того чтобы познакомиться с Cucumber - популярной системой предназначенной для разработки через поведение (Раздел 8.3). В частности, мы перепишем пару интеграционных RSpec тестов на Cucumber для сравнения этих двух методик.

Как и в предыдущих главах, мы будем делать нашу работу в новой ветке и объединим изменения в конце:

$ git checkout -b sign-in-out

Сессии и провальный вход

Сессия это полупостоянное соединение между двумя компьютерами, такими как клиентский компьютер с запущенным веб-браузером и сервер с запущенными на нем Rails. Есть несколько моделей поведения сессий, принятых в сети: "забывание" сессии при закрытии браузера, опциональное использование "запомнить меня" флажка для постоянных сессий, и запоминание сессий до явных признаков выхода пользователя из системы.1 Мы выберем последнюю из этих опций: когда пользователь войдет, мы запомним его статус вошедшего "навсегда" и очистим сесию только после явного выхода пользователя из системы. (Мы увидим в Разделе 8.2.1 насколько продолжительно это самое "навсегда".)

Удобно моделировать сессии как RESTful ресурс: у нас будет страница входа для новых сессий, вход будет создавать сессию и выход будет уничтожать ее. В отличие от ресурса Users который использует базу данных (через модель User) для сохранения данных, ресурс Sessions будет использовать куки, которые представляют собой небольшой фрагмент текста, помещаемого в браузер пользователя. Большая часть сложностей в разработке системы входа связана с построением этого, опирающегося на куки, аутентификационного механизма. В этом и последующих разделах мы будем заниматься подготовительной работой - создадим контроллер Sessions , форму входа и соответствующие действия контроллера. Затем мы завершим вход пользователей написав необходимый для манипуляций с куки код в Разделе 8.2.

Sessions контроллер

Элементы системы входа и выхода соответствуют определенным REST действиям Sessions контроллера: форма входа обрабатывается new действием (рассматривается в этом разделе), сам вход обрабатывается отправкой запроса POST к действию create (Раздел 8.1 и Раздел 8.2) и выход обрабатывается отправкой запроса DELETE к действию destroy (Раздел 8.2.6). (Вспомним о соответствии между глаголами HTTP и REST действиями из Таблицы 7.1.) Для начала мы сгенерируем контроллер Sessions и интеграционный тест для механизма аутентификации:

$ rails generate controller Sessions --no-test-framework
$ rails generate integration_test authentication_pages

Следуя модели из Раздела 7.2 для страницы регистрации, мы создадим форму входа для создания новых сессий ( рис. 8.1).

Набросок формы входа.

Рис. 8.1. Набросок формы входа.

Страница входа живет по URL предоставленному signin_path (уже определенному) и, как обычно, мы начнем с минималистичного теста как это показано в Листинге 8.1. (Сравните его с аналогичным кодом для страницы регистрации из Листинга 7.6.)

require 'spec_helper'

describe "Authentication" do

  subject { page }

  describe "signin page" do
    before { visit signin_path }

    it { should have_content('Sign in') }
    it { should have_title('Sign in') }
  end
end
Листинг 8.1. Тесты для new session действия и представления. spec/requests/authentication_pages_spec.rb

Изначально тест провальный, как и требуется:

$ bundle exec rspec spec/

Для того чтобы получить прохождение тестов из Листинга 8.1, в первую очередь нам необходимо определить маршруты для ресурса Sessions, совместно с кастомным именованным маршрутом для страницы входа (который мы направим к действию new контроллера Session). Как и с ресурсом Users, мы можем использовать метод resources для определения стандартных RESTful маршрутов:

resources :sessions, only: [:new, :create, :destroy]

Поскольку нам нет надобности показывать или редактировать сессии, мы ограничимся действиями new, create и destroy с помощью опции :only принимаемой resources. Конечный результат, включающий именованные маршруты для входа и выхода, представлен в Листинге 8.2.

SampleApp::Application.routes.draw do
  resources :users
  resources :sessions, only: [:new, :create, :destroy]
  root  'static_pages#home'
  match '/signup',  to: 'users#new',            via: 'get'
  match '/signin',  to: 'sessions#new',         via: 'get'
  match '/signout', to: 'sessions#destroy',     via: 'delete'
  .
  .
  .
end
Листинг 8.2. Добавление ресурса для получения стандартных RESTful действий для сессий.config/routes.rb

Обратите внимание на использование via: ’delete’ для маршрута выхода, указывающее на то, что он должен быть вызван с помощью HTTP запроса DELETE.

Ресурсы, определенные в Листинге 8.2 обеспечивают URL-адреса и действия, аналогичные ресурсу Users (Таблица 7.1), как видно в Таблице 8.1. Обратите внимание на то что маршруты для входа и выхода являются кастомными, но маршрут для создания сессии остался дефолтным (т.е., [resource name]_path).

Таблица 8.1. RESTful маршруты, обеспеченные правилами в Листинге 8.2.
HTTP запрос URL Именованный маршрут Действие Цель (назначение)
GET /signin signin_path new страница для новой сессии (вход)
POST /sessions sessions_path create создание новой сессии
DELETE /signout signout_path destroy удаление сессии (выход)

Кстати, для генерации полного списка маршрутов вашего приложения вы можете использовать команду rake routes:

$ rake routes
        Prefix Verb   URL Pattern                    Controller#Action
         users GET    /users(.:format)               users#index
               POST   /users(.:format)               users#create
      new_user GET    /users/new(.:format)           users#new
     edit_user GET    /users/:id/edit(.:format)      users#edit
          user GET    /users/:id(.:format)           users#show
               PATCH  /users/:id(.:format)           users#update
               PUT    /users/:id(.:format)           users#update
               DELETE /users/:id(.:format)           users#destroy
      sessions POST   /sessions(.:format)            sessions#create
   new_session GET    /sessions/new(.:format)        sessions#new
       session DELETE /sessions/:id(.:format)        sessions#destroy
          root GET    /                              static_pages#home
        signup GET    /signup(.:format)              users#new
        signin GET    /signin(.:format)              sessions#new
       signout DELETE /signout(.:format)             sessions#destroy
          help GET    /help(.:format)                static_pages#help
         about GET    /about(.:format)               static_pages#about
       contact GET    /contact(.:format)             static_pages#contact

Следующим шагом необходимым для прохождения тестов из Листинга 8.1 является добавление new действия к контроллеру Sessions, как это показано в Листинге 8.3 (который также определяет create и destroy действия для использования в будущем).

class SessionsController < ApplicationController

  def new
  end

  def create
  end

  def destroy
  end
end
Листинг 8.3. Начальный контроллер Sessions. app/controllers/sessions_controller.rb

Последний шаг это определение начальной версии страницы входа. Обратите внимание, что, поскольку это страница для новой сесии, она живет в файле app/views/sessions/new.html.erb, который мы должны создать. Содержимое, которое в настоящий момент определяет только заголовок страницы и заголовок первого уровня, представлено в Листинге 8.4.

<% provide(:title, "Sign in") %>
<h1>Sign in</h1>
Листинг 8.4. Начальное представление входа. app/views/sessions/new.html.erb

Теперь тесты из Листинга 8.1 должны пройти и мы готовы к созданию самой формы входа.

$ bundle exec rspec spec/
Вадим Обозин
Вадим Обозин

Здравствуйте, записался на курс. При этом ставил галочку на "обучаться с тьютором". На email пришло письмо, о том, что записался на самостоятельное изучение курса. Как выбрать тьютора?

Акбар Ахвердов
Акбар Ахвердов
Россия, г. Москва
Артём Зайцев
Артём Зайцев
Украина, ДНР