Здравствуйте, записался на курс. При этом ставил галочку на "обучаться с тьютором". На email пришло письмо, о том, что записался на самостоятельное изучение курса. Как выбрать тьютора? |
Войти, выйти
Теперь, когда новые пользователи могут регистрироваться на нашем сайте (Глава 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).
Страница входа живет по 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).
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/