Статьи

Начало работы с Restful-аутентификацией в Rails

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

Эта часть урока очень проста. Все, что мы сделаем, — это создадим простое приложение, которое позволит пользователям создавать / читать / обновлять / уничтожать записи, содержащие название фильма и рейтинг. Я рекомендую опытным разработчикам Rails перейти к третьему разделу, где начинается код restful_authentication. Примечание . Я сделал все это в OSX на Mac, но это не зависит от платформы, хотя вам может понадобиться добавить ./ к некоторым командам, чтобы Windows знала, что вы смотрите в текущем каталоге. Если у вас есть какие-либо проблемы, я могу помочь в комментариях или в твиттере ( @noahhendrix )

Сначала нам нужно, чтобы rails генерировали код фреймворка. Это делается путем выполнения «rails MyMovies» в терминале. «MyMovies» — это то, что я выбрал, чтобы назвать это приложение, и вы можете использовать это или что-то еще. Эта команда создает множество файлов и папок, большинство из которых нам не нужны, но необходимы для правильной работы приложения. После этого вы захотите перейти во вновь созданный каталог MyMovies.

1
2
rails MyMovies
cd MyMovies

Теперь мы собираемся использовать возможности генераторов Rails для быстрого создания кода и базы данных для нашего основного приложения. В терминале выполните команду:

1
script/generate scaffold Movie title:string rating:integer

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

1
rake db:migrate

Эта команда настроит базу данных и создаст таблицу с соответствующими столбцами. Примечание : Rails достаточно умен, чтобы включить для нас столбцы ID, create_at и updated_at. Он также будет управлять этими столбцами, поэтому нам, как разработчикам, никогда не нужно писать код, который устанавливает эти столбцы. Теперь давайте посмотрим на плоды нашего труда, верно, мы написали ноль строк кода, но у нас уже есть работающее приложение. Запустите сервер и перейдите по адресу http: // localhost: 3000 / movies . В вашем терминале введите:

1
script/server

Вы можете попробовать создать фильм и рейтинг и посмотреть, как легко все работает. Команда rails действительно проделала огромную работу, позволив разработчикам быстро выдвинуть код без особых усилий, однако этот код является лишь отправной точкой. Разработчики должны сами сделать это и добавить дополнительные функции и настройки для пользователей. Хорошее место для начала — определитесь, есть ли какие-то ограничения, которые мы наложим на нашу модель. В этом примере я решил, что нам потребуется, чтобы пользователь мог оценивать фильм только от 1 до 5 звезд. Хотя есть и другие, например, убедиться, что они вводят название фильма и что рейтинг — это число. Вот как это делается в файле модели фильма.

1
# app/models/movie.rb validates_presence_of :title, :rating validates_numericality_of :rating, :greater_than_or_equal_to => 1, :less_than_or_equal_to => 5

Синтаксис, используемый в Rails, часто многословен, но также достаточно описателен. Однако положительным побочным эффектом является то, что мы никогда не должны угадывать, что означает код, который мы пишем. Мы сообщаем rails, прежде чем он сохранится в базе данных, чтобы убедиться, что заголовок и рейтинг установлены, и в качестве дальнейшего ограничения рейтинга должно быть число от 1 до 5. Обратите внимание, что на рисунках ниже мы получаем встроенный отчет об ошибках по умолчанию в rails ,

Даже будучи разработчиками, мы не освобождаемся от рассмотрения интерфейса разрабатываемых нами приложений. Пользователи должны немедленно понять цель любой информации, отображаемой им. В нашем приложении они устанавливают рейтинг, но мы никогда не говорим, что это должно быть целое число от 1 до 5. Сначала пользователь может ввести строку, например, «хорошо». Мы могли бы поставить метку рядом с текстовым полем, в котором перечислены наши требования, но мы можем пойти дальше и явно позволить им выбирать нужные нам опции в формате переключателей. Часть методологии рельсов Model View Controller (MVC) говорит нам, что это изменение представления, и оно находится в каталоге представлений. Сначала мы должны удалить строку, которая создает текстовое поле и его метку, вокруг строки 11 в моем коде. Ищу:

1
2
3
4
# app/views/movies/edit.html.erb
# app/views/movies/new.html.erb
 
<%= f.text_field :rating %>

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

1
2
3
4
5
6
7
8
9
# app/views/movies/edit.html.erb
# app/views/movies/new.html.erb
 
Rating<br />
<%= f.radio_button :rating, 1 %> <%= f.label :rating_1, «1» %>
<%= f.radio_button :rating, 2 %> <%= f.label :rating_2, «2» %>
<%= f.radio_button :rating, 3 %> <%= f.label :rating_3, «3» %>
<%= f.radio_button :rating, 4 %> <%= f.label :rating_4, «4» %>
<%= f.radio_button :rating, 5 %> <%= f.label :rating_5, «5» %>

Хорошо, теперь давайте внимательно посмотрим на этот код. Сначала мы даем оценочную метку, чтобы сообщить пользователю, для чего нужны переключатели, затем мы перемещаем действительные кнопки. Первый параметр для метода radio_button — это атрибут модели, с которым мы работаем, а второй — это значение, которое отправляется на сервер при отправке. После каждой кнопки у нас есть метка, чтобы сообщить пользователю, какой оценке соответствует данная кнопка, поэтому мы передаем: rating_ #, потому что когда rails генерирует переключатель, он присваивает ему идентификатор attribute_value, поэтому наши метки подключаются к этим значениям. Вот как выглядит новая форма с переключателями, обратите внимание, что при редактировании фильма Rails автоматически выбирает переключатель, который соответствует предыдущему рейтингу из базы данных.

На этом заканчивается первая часть. В предыдущем разделе мы использовали наше приложение для оценки фильмов, используя возможности генераторов Rails и некоторый базовый код вида. В следующем разделе мы сделаем наше приложение немного более визуально привлекательным, добавив немного графики и CSS-код для улучшения представления.

Сначала мы рассмотрим макеты. Не вдаваясь в подробности макетов деталей, удерживайте элементы на странице, которые не меняются в приложении. Например, заголовок, нижний колонтитул и навигация, как правило (и это справедливо), остаются неизменными для всего веб-сайта. Хотя наше приложение довольно тривиально, макет важен для согласованности.

Наша цель — разместить логотип, фоновое изображение и центрированный контейнер для содержимого на каждой странице. Сначала нам нужно создать нашу страницу с широким макетом приложения. Rails достаточно любезен для создания файла макета в масштабе модели, но это не будет применяться к пользовательским страницам, когда мы перейдем к этой части, поэтому нам нужно изменить его на глобальный макет. Это легко сделать, переименовав app / views / layouts / movies.html.erb в app / views / layouts / application.html.erb . Теперь нам нужно открыть файл и внести несколько изменений. Сначала давайте обернем весь наш контент в div, называемый pagewrap. Это хорошо, потому что это дает нам немного больше контроля над CSS, чем мы обычно получаем только с body. Это можно сделать, просто включив отверстие чуть ниже тела и закрыв его прямо над закрытием тела.

01
02
03
04
05
06
07
08
09
10
11
12
# app/views/layouts/application.html.erb
<body>
 
  <div id=»pagewrap»>
 
    …
 
  </div>
 
</body>

Пока мы в этом файле, давайте продолжим и добавим файл CSS, который мы создадим следующим. Найдите строку (9 в моем файле), которая говорит stylesheet_link_tag ‘scaffold’ и добавьте к ней запятую и ‘style’, чтобы она выглядела так:

1
2
3
# app/views/layouts/application.html.erb
 
<%= stylesheet_link_tag ‘scaffold’, ‘style’ %>

Это вспомогательный метод, предоставляемый rails, который создает для нас тег <link> и ссылается на файл style.css в нашем каталоге стилей. В public / stylesheets создайте файл с именем style.css . Поскольку это не совсем урок CSS, я буду только задевать объявления.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
# public/stylesheets/style.css
 
html, body {
  background-image: url(/images/bg_body.jpg);
}
 
#pagewrap {
  -moz-border-radius: 10px;
  -webkit-border-radius: 10px;
 
  margin: 0 auto;
  padding: 10px;
  width: 900px;
 
  /* logo */
  background: white url(/images/logo.png) no-repeat;
  padding-top: 72px;
}

Я применяю фоновое изображение к тегу body, чтобы оно охватывало всю страницу. Элемент pagewrap, который мы создали ранее, имеет закругленные углы (только в Safari и Firefox, извините, IE), по центру используется поле margin, некоторые отступы для вставки содержимого, ширина 900px и неповторяющееся фоновое изображение, которое является нашим логотипом. Контент отодвигается на 72 пикселя, чтобы разместить логотип, что-то вроде хака, но простой способ разместить логотип на странице. К сожалению, я не могу распространять фоновое изображение или логотип, потому что они были созданы с использованием частей из Шаблон, который я купил на ThemeForest.net . Но разве это не выглядит намного лучше сейчас?

Индексная страница довольно хромая как есть, потому что она просто перечисляет название фильма и целое число для рейтинга. Давайте добавим немного магии CSS, чтобы сделать ее немного более красивой. Сначала давайте обновим app / views / movies / index.html.erb , изменив ячейку таблицы, в которой хранится рейтинг, в строке 12. Мы заключим рейтинг в элемент div с помощью специального класса, содержащего количество отображаемых звездочек. Мы также используем вспомогательный метод множественного числа в Rails, который выведет переданное ему число и добавит слово после числа, сделав его множественным, если это необходимо.

1
2
3
# app/views/movies/index.html.erb
 
<td><div class=»star-<%=h movie.rating %> rating»><%=pluralize movie.rating, «Star» %></div></td>

Хорошо, теперь давайте создадим необходимый код CSS. Примечание . Я не включил изображение в разметку HTML, так что если у пользователя отключен CSS, он не увидит звездочек, только что-то вроде «3 звезды».

01
02
03
04
05
06
07
08
09
10
11
12
13
# public/stylesheets/style.css
 
.rating {
  background: url(/images/stars.png) no-repeat;
  height: 26px;
  overflow: hidden;
  text-indent: -1000px;
}
.star-5 { width: 115px;
.star-4 { width: 92px;
.star-3 { width: 69px;
.star-2 { width: 46px;
.star-1 { width: 23px;

Мы применяем неповторяющееся фоновое изображение из 5 звезд ко всем элементам рейтинга, а затем изменяем ширину (с шагом 20%), чтобы позволить скрытие звезд. Мы также выталкиваем текст «# Звезд» со страницы с отрицательным отступом, так как звезды — достаточно простой язык, чтобы не требовать реальных слов.

Последнее, что я сделал, это удалил ссылки на страницу шоу. Было немного излишне включать его, поскольку на индексной странице отображалась вся соответствующая информация. Вы можете сделать то же самое, открыв app / views / movies / index.html.erb и удалив строку 13, ссылку_ на страницу показа. Вам также необходимо открыть app / controllers / movies_controller.rb и обновить строки 48 и 65, вызывая redirect_to, вместо этого укажите параметр movies_url .

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

Перво-наперво нам нужно установить плагин restful_authentication, остановить сервер, нажав CTRL + C , если он у вас запущен. Страница GitHub для restful_authentication содержит инструкции по установке, но вот дайджест-версия читателя:

1
script/plugin install http://github.com/technoweenie/restful-authentication.git restful_authentication

Передача второй restful_authentication говорит rails, что мы хотим переименовать папку плагинов в нее вместо restful-authenticatoin . Аргументация объясняется под заголовком Установка для тех, кто заинтересован. Хорошо, теперь эти команды в основном отправляются на сервер и извлекают все файлы restful_authentication и копируют их на ваш сервер. Он также создает скрипт генератора, который мы можем запустить для создания необходимых контроллеров, моделей и представлений в нашей папке приложения.

1
script/generate authenticated user sessions —include-activation

Используя аутентифицированный генератор, мы говорим, что хотим вызвать модель, которая обрабатывает информацию о пользователе, пользователя, и модель, которая обрабатывает сеансы, называемые сессиями, достаточно просто, верно? Но почему мы их разделяем? Если вы действительно задумываетесь об этом, регистрация пользователей и получение их информации аналогичны аутентификации, но на самом деле это тоже совсем другое. Один хранит информацию о долгосрочном входе в систему пользователя, а другой является более временным и удаляется после выхода из системы. Так что действительно имеет смысл разделить их в нашем дизайне. Мы также передаем флаг include-активации, чтобы он генерировал необходимые почтовые программы для активации учетных записей пользователей по электронной почте.

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

1
script/generate migration add_user_id_to_movie user_id:integer

Rails 2.0 принес некоторую изящную автоматизацию для генератора миграций. Из-за имени схемы add_user_it_to_movie rails будет предполагать, что мы имеем в виду, и создаст схему, которая сделает именно это. Так что нам остается только бежать

1
rake db:migrate

и наша база данных обновлена. Если вы хотите посмотреть созданный им файл миграции, чтобы почувствовать, что мы сделали, вы можете проверить app / db / migrate и найти файл add_user_id_to_movie.rb. Хорошо, мы все сделали в терминале на данный момент!

Чтобы пользователь «владел» записью фильма, мы должны сообщить рельсам, что они делают. Это из-за ассоциаций, объявленных в моделях. Сначала давайте обновим пользовательскую модель, созданную нашим генератором, app / models / user.rb. Я обычно добавляю следующий код прямо над первым объявлением метода, активируйте в этом случае.

1
2
3
#app/models/user.rb
 
has_many :movies

Это довольно просто, мы сообщаем, что у пользователя есть много записей фильмов, связанных с его учетной записью. Теперь давайте сообщим модель фильма сотрудника в app / models / movie.rb . Добавьте этот код прямо под наши методы проверки ранее.

1
2
3
#app/models/movie.rb
 
belongs_to :user

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

Это скорее изменение пользовательского интерфейса, но оно должно быть включено во все приложения. В настоящее время, если пользователь хочет зарегистрировать URL-адрес, например example.com/users/new, или если он хочет войти, это example.com/sessions/new , на мой взгляд, это немного многословно. Таким образом, мы добавим несколько пользовательских маршрутов в config / rout.rb , мой был в строке 8.

1
2
3
4
5
6
# config/routes.rb
 
map.activate ‘/activate/:activation_code’, :controller => ‘users’, :action => ‘activate’
map.signup ‘/signup’, :controller => ‘users’, :action => ‘new’
map.login ‘/login’, :controller => ‘sessions’, :action => ‘new’
map.logout ‘/logout’, :controller => ‘sessions’, :action => ‘destroy’

Как вы можете видеть здесь, мы объявляем active, signup, login и logout для их очевидных аналогов. Это сделает наши URL более понятными и легко запоминающимися. Обратите внимание, что для активации требуется строка, в которой будет указан код активации, который мы рассылаем по электронной почте пользователям для проверки адресов электронной почты.

На многих веб-сайтах обычно требуется, чтобы пользователи проверяли адреса электронной почты, щелкая ссылку в письме, отправляемом на указанный адрес. В других языках и средах это несколько сложно, но плагин restful_authentication и rails mailers делает эту расширенную функциональность очень простой. Однако прежде чем мы сможем отправлять электронные письма, нам нужно сообщить rails, как проходить аутентификацию на нашем SMTP-сервере. Теперь это варьируется от хоста к хосту, и вам нужно будет связаться с вашим хостом, чтобы определить, как заполнить эти настройки. Примечание. Сначала вам нужно будет создать файл config / initializers / mail.rb.

01
02
03
04
05
06
07
08
09
10
11
# config/initializers/mail.rb
 
ActionMailer::Base.delivery_method = :smtp
ActionMailer::Base.smtp_settings = {
    :address => «mail.example-domain.com»,
    :port => 25,
    :domain => «www.example-domain.com»,
    :authentication => :login,
    :user_name => «user@example-domain.com»,
    :password => «secret»
}

Пока мы устанавливаем настройки по умолчанию, мы должны определить URL нашего веб-сайта, который мы будем использовать во всех наших электронных письмах. Вы можете задаться вопросом, зачем мы это делаем, и причина в том, чтобы наше приложение было на будущее. Например, когда мы разрабатываем локально, домен является localhost: 3000 / , и, вероятно, наш производственный домен отличается. Если в какой-то момент в будущем производственный URL изменится, мы можем обновить его в одном месте и не беспокоиться о других пропущенных других изменениях. Но где мы размещаем эти значения по умолчанию? Rails создал несколько файлов конфигурации, которые загружаются в зависимости от среды, в которой мы находимся: разработка, тестирование или производство. Начиная с разработки, мы должны изменить config / environment / development.rb , строка 19.

1
2
3
# config/environments/development.rb
 
SITE_URL = «localhost:3000»

И аналогично в config / средах / production.rb в строке 30.

1
2
3
# config/environments/production.rb
 
SITE_URL = «noahhendrix.com»

Теперь нам нужно обновить файл модели user_mailer, app / models / user_mailer.rb , чтобы использовать нашу вновь созданную переменную SITE_URL . Эти изменения необходимо внести в строки 6, 13 и 20, удалить YOURSITE и заменить его на # {SITE_URL} . В строке 19 вам нужно обновить адрес электронной почты на свой собственный.

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

Они также получают электронное письмо после активации учетной записи.

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

Мы собираемся обновить несколько строк, и, поскольку они почти одинаковые, мы не будем рассматривать их по отдельности. По сути, мы говорим контроллеру ограничить все находки переменной current_user (т. Е. Добавить «WHERE user_id = ##» в запросы к базе данных). Переменная current_user — это то, что restful_authentication дает нам использовать в нашем контроллере по этой конкретной причине. Я включил приблизительные номера строк, но если они отличаются для вас, знайте, что мы только модифицируем методы: индексировать, показывать, редактировать, создавать, обновлять и уничтожать. Примечание : переменная Movie заменена на фильмы, потому что мы обращаемся к ней через пользователя, который появляется, когда мы говорим, что у пользователя много фильмов.

1
2
3
4
5
6
7
8
# app/controllers/movies_controller.rb
 
Line 8: @movies = current_user.movies
Line 20: @movie = current_user.movies.find(params[:id])
Line 42: @movie = current_user.movies.find(params[:id])
Line 49: @movie = current_user.movies.create(params[:movie])
Line 67: @movie = current_user.movies.find(params[:id])
Line 85: @movie = current_user.movies.find(params[:id])

Пока этот файл открыт сверху (строка 2), нам нужно добавить фильтр before. Это говорит rails, что перед тем, как он запустит любой из методов ниже, он должен сначала запустить предоставленный нами метод.

1
2
3
# app/controllers/movies_controller.rb
 
before_filter :login_required

Метод: login_required предоставляется плагином и в основном проверяет, вошел ли пользователь в систему, а если нет, перенаправляет его на страницу входа.

Хорошо, не так уж плохо, правда? У нас есть несколько последних вещей, которые нужно очистить, прежде чем плагин полностью заработает. В app / controllers / session_controller.rb и app / controllers / users_controller.rb есть оператор включения, который необходимо удалить и добавить в app / controllers / application_controller.rb . Эта строка содержит необходимые внутренние файлы, которые плагин использует для управления пользователями и сессиями. Я поставил свой в строке 5.

1
2
3
# app/controllers/application_controller.rb
 
include AuthenticatedSystem

Если вам интересно, откуда он извлекает этот файл, вы можете найти его в lib / authenticated_system.rb .

Нам нужно добавить два метода в контроллер пользователей, app / controllers / users_controllers.rb , которые позаботятся о форме, чтобы пользователи могли сбросить свой пароль. Однако, прежде чем мы углубимся в код, давайте на секунду обсудим наш план. Мы хотим, чтобы пользователь мог щелкнуть ссылку, которая говорит, что он забыл свой пароль. Затем они будут перенаправлены на страницу с вопросом, какой адрес электронной почты они использовали при создании своей учетной записи, после того как они подтвердят, что мы отправим электронное письмо со ссылкой, содержащей специальный хеш, по которому они смогут щелкнуть. Как только они откроют ссылку в своем электронном письме, у них будет форма для выбора пароля и его подтверждения.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# app/controllers/users_controller.rb
 
def forgot
  if request.post?
    user = User.find_by_email(params[:user][:email])
 
    respond_to do |format|
      if user
        user.create_reset_code
        flash[:notice] = «Reset code sent to #{user.email}»
 
        format.html { redirect_to login_path }
        format.xml { render :xml => user.email, :status => :created }
      else
        flash[:error] = «#{params[:user][:email]} does not exist in system»
 
        format.html { redirect_to login_path }
        format.xml { render :xml => user.email, :status => :unprocessable_entity }
      end
    end
 
  end
end

Вот действие со ссылкой на форму, где они вводят свой адрес электронной почты. Сначала мы проверяем, отправили ли они форму через почтовый запрос, если нет, мы ничего не делаем, потому что ждем отправки формы. После того, как у нас есть данные публикации, мы находим информацию о пользователях, используя предоставленный адрес электронной почты, чтобы убедиться, что они существуют. Мы используем блок формата response_to, чтобы мы могли отвечать на запросы как xml, так и html. В этом уроке мы не будем использовать формат XML, но хорошо бы предоставить его для веб-сервисов. Теперь вы увидите, что у нас есть простое выражение if, говорящее, что если пользователь не nil (подразумевается), мы хотим запустить метод create_reset_code и уведомить пользователя с помощью метода flash, что им нужно проверить свою электронную почту на наличие ссылки для сброса. Если пользователь не существует, мы сообщаем ему об этом.

Хорошо, теперь давайте посмотрим на действие сброса, которое они видят после того, как щелкнули ссылку в своем электронном письме.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
# app/controllers/users_controller.rb
 
def reset
  @user = User.find_by_reset_code(params[:reset_code]) unless params[:reset_code].nil?
  if request.post?
    if @user.update_attributes(:password => params[:user][:password], :password_confirmation => params[:user][:password_confirmation])
      self.current_user = @user
      @user.delete_reset_code
      flash[:notice] = «Password reset successfully for #{@user.email}»
      redirect_to root_url
    else
      render :action => :reset
    end
  end
end

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

Отчасти это сбивает с толку, потому что мы не полностью реализовали некоторые методы, которые мы использовали, такие как: create_reset_code, недавно_reset? И delete_reset_code. Мы поместим их в пользовательскую модель app / models / user.rb прямо над словом, защищенным снизу.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
#app/models/user.rb
 
#reset methods
def create_reset_code
  @reset = true
  self.attributes = {:reset_code => Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )}
  save(false)
end
 
def recently_reset?
  @reset
end
 
def delete_reset_code
  self.attributes = {:reset_code => nil}
  save(false)
end

В функции create_reset_code мы устанавливаем переменную экземпляра @reset в значение true, чтобы наше приложение могло помнить, что они недавно сбросили свой пароль. Затем мы устанавливаем reset_code в базе данных (для которого мы еще добавили столбец) в хеш текущего времени, чтобы сделать его уникальным. Затем мы сохраняем этот reset_code в базе данных, параметр false указывает rails игнорировать проверки, которые мы можем сделать, потому что мы сами устанавливаем код, а не пользователей.

Недавний сброс? Метод просто так, мы можем проверить, является ли переменная @reset истинным или ложным. И, наконец, delete_reset_code делает именно то, что выводит его имя, удаляя код сброса из базы данных.

Мы поговорили о формах, которые пользователь должен заполнить, чтобы сбросить свой пароль, давайте начнем и создадим их. Сначала в каталоге app / views / users / с именем Forgot.html.erb .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
# app/views/users/forgot.html.erb
 
<%= error_messages_for :user %>
 
<% form_for :user do |f|
  <p>
    Reset Password Request
  </p>
 
 
  <p>
    <label for=»email»>Email</label><br />
    <%= f.text_field :email %>
  </p>
 
 
  <p>
    <%= link_to «Cancel», login_path %>
    <%= submit_tag ‘Submit’ %>
  </p>
<% end -%>

Это довольно общая форма, в которой мы просим пользователя отправить ему электронное письмо. Теперь нам нужно создать форму для сброса пароля в том же каталоге, что и выше, создать файл с именем reset.html.erb .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# app/views/users/reset.html.erb
 
<%= error_messages_for :user %>
 
<% form_for :user do |f|
  <p>
    Pick a new password for <span><%= @user.email %>
  </p>
 
  <p>
    <label for=»password»>Password</label><br />
    <%= f.password_field :password %>
  </p>
 
  <p>
    <label for=»password»>Confirm Password</label><br />
    <%= f.password_field :password_confirmation %>
  </p>
 
 
  <p>
    <%= submit_tag ‘Reset’ %>
  </p>
<% end -%>

Хорошо, теперь, когда у нас есть настройка форм, мы можем изменить класс почтовой программы для отправки ссылок на сброс. Так как мы работаем с представлениями, давайте начнем там. Если традиционное представление примерно переводится на веб-страницу, то представление почтовой программы будет переводиться на электронную почту. По сути, это базовая схема электронного письма, которое мы хотим отправить пользователю, оставляя места для переменных, которые предоставит наш контроллер. Файл, который мы собираемся создать, это app / views / user_mailer / reset_notification.html.erb и вставьте этот код:

1
2
3
4
5
6
7
8
# app/views/user_mailer/reset_notification.html.erb
 
Request to reset password received for <%= @user.login %>
 
Visit this url to choose a new password:
 
<%= @url %>
(Your password will remain the same if no action is taken)

Это все довольно общая разметка, обратите внимание, что у нас есть место для URL и имени пользователя для входа. Мы также говорим им, что если они ничего не сделают, пароль не будет изменен. Теперь давайте перейдем в app / models / user_mail.rb и создадим метод reset_notification, здесь наше представление получает логин пользователя и сбрасывает переменные url. Вставьте этот код куда-нибудь после метода активации, но выше защищенного.

1
2
3
4
5
6
7
8
9
# app/models/user_mailer.rb
 
def reset_notification(user)
  setup_email(user)
  @subject += ‘Link to reset your password’
  @body[:url] = «#{SITE_URL}/reset/#{user.reset_code}»
end

Здесь мы делаем несколько вещей. Сначала мы вызываем метод setup_email, который объявлен как защищенный (защищенный означает, что только этот класс может вызывать этот метод), что является некоторыми общими настройками электронной почты. Мы добавляем строку к теме, чтобы помочь пользователю определить цель письма от темы. Затем мы устанавливаем тот URL, о котором мы говорили ранее, заметив, что мы используем переменную # {SITE_URL}, которую мы установили ранее. Переменная reset_code происходит от пользовательской модели, которую мы установили ранее с помощью метода create_reset_code, помните?

Еще одна модель, которую нам нужно обновить, app / models / user_observer.rb . По сути, это то, что мы говорим, чтобы отправить это письмо, если переменная @reset равна true. Эти методы выполняются всякий раз, когда с пользователем взаимодействуют, в этом случае после того, как мы сохраним его в базе данных, мы хотим проверить, нужно ли ему получать электронное письмо с сообщением об изменении. После строки, которая отправляет письмо активации, добавьте этот код.

1
2
3
4
5
# app/models/user_observer.rb
 
UserMailer.deliver_reset_notification(user) if user.recently_reset?

Хорошо, давайте обновим еще один файл, прежде чем мы перейдем в терминал, чтобы добавить столбец reset_code. Помните, что URL-адрес, который пользователь получил по электронной почте? Ну, нам нужно установить это в файле маршрутов, config / rout.rb. В строке ниже, где мы настраиваем маршрут выхода из системы, добавьте эти две строки:

1
2
3
4
# config/routes.rb
 
map.forgot ‘/forgot’, :controller => ‘users’, :action => ‘forgot’
map.reset ‘reset/:reset_code’, :controller => ‘users’, :action => ‘reset’

Это похоже на приведенные выше маршруты, мы сопоставляем дружественные URL-адреса с соответствующими контроллерами и действиями. Хорошо, давайте перейдем в терминал. Итак, теперь мы собираемся использовать тот же генератор миграции, который мы использовали для добавления user_id в таблицу фильмов. Так что этот код будет выглядеть действительно знакомым.

1
script/generate migration add_reset_code_to_user reset_code:string

Не забудь бежать

1
rake db:migrate

обновить базу данных.

Сначала нам нужно удалить файл public / index.html . Это страница, которая переопределит контроллер по умолчанию, который мы установим. Затем откройте файл config / rout.Rb и раскомментируйте строку 46 (начинающую map.root ). Измените добро пожаловать на фильм , это говорит рельсам, что рендеринг контроллера фильма должен быть установлен по умолчанию, если кто-то переходит на localhost: 3000 / или в корень нашего рабочего домена.

Хорошо, теперь мы можем перейти к представлениям. в app / views / session / new.html.erb мы должны удалить строки 8 и 11, чтобы позволить нашим пользователям помнить свой сеанс, чтобы им не приходилось повторно входить в систему каждый раз, когда они посещают. В строке 11 есть тег абзаца, который нам нужен, чтобы добавить ссылку регистрации и забыть пароли внутри этого элемента.

1
2
3
4
# app/views/sessions/new.html/erb
 
<%= link_to ‘Sign Up’, :signup %>
<%= link_to ‘Forgot Password?’, :forgot %>

Теперь пользователи могут легко зарегистрироваться, войти в систему или сбросить свой пароль, но нам нужно включить ссылку выхода из системы после того, как они вошли в систему, чтобы они могли завершить свой сеанс. Мы сделаем это в файле макета приложения app / views / layouts / application.html.erb , чтобы он отображался на всех страницах. Я добавил свой прямо над флеш-сообщениями, но под div обтекания страниц.

1
2
3
4
# app/views/layouts/application.html.erb
<div class=»user»>
  <%= link_to «Logout», :logout unless !logged_in?
</div>

Обратите внимание, что мы говорим, чтобы включить ссылку, если logged_in? ложно, logged_in? является помощником, предоставляемым плагином, который возвращает true, если пользователь вошел в систему, и false, если они не зарегистрированы, и, конечно, мы хотим получить ссылку только в том случае, если они действительно вошли в систему.

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

1
2
3
# app/views/layouts/application.html.erb
 
<p style=»color: red»><%= flash[:alert] %></p>

Возможно, вы заметили, что при регистрации приложение входит в систему, не требуя сначала проверки адреса электронной почты, хотя это предотвращает любые последующие входы в систему. Это то, что включено в пользовательский контроллер по умолчанию, где, если пользователь проходит проверку, он регистрируется, устанавливая current_user для только что созданной пользовательской модели. В нашем приложении это ошибка, потому что мы хотим, чтобы пользователь сначала аутентифицировал свой адрес электронной почты. Мы можем исправить это поведение в app / controllers / users_controller.rb около строки 16, которую нам нужно удалить или закомментировать, где он устанавливает current_user = @user .

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

1
2
3
# app/controllers/users_controller.rb
 
flash[:notice] = «Thanks for signing up! Please check your email to activate your account.»

Хорошо, это все, что нам нужно сделать для нашей базовой аутентификации пользователя. Я надеюсь, что это помогло как пошаговое руководство по установке и использованию restful_authentication в вашем приложении rails. Я знаю, что это очень упрощенно, поэтому оставьте любые желаемые настройки в комментариях или свяжитесь со мной в твиттере ( @noahhendrix ), и я постараюсь решить их. Спасибо за прочтение!