Статьи

Rails: аутентификация пользователя / пароля с нуля, часть II

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

Метод аутентификации

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

Давайте напишем метод аутентификации в пользовательской модели

def self.authenticate(username_or_email=«», login_password=«»)
if EMAIL_REGEX.match(username_or_email)
user = User.find_by_email(username_or_email)
else
user = User.find_by_username(username_or_email)
end
if user && user.match_password(login_password)
return user
else
return false
end
end
def match_password(login_password=«»)
encrypted_password == BCrypt::Engine.hash_secret(login_password, salt)
end

view raw
user.rb
hosted with ❤ by GitHub

Мы видим, что если логин_пароль совпадает, мы возвращаем объект пользователя. В противном случае метод возвращает false.


Sessions Controller и шаблон формы входа

С нашим методом аутентификации давайте создадим контроллер сессий . Контроллер SessionsController будет создавать действия входа, дома, профиля и настройки.

$ rails g controller sessions login, home, profile, setting

Давайте сосредоточимся на шаблоне входа . Акт входа в систему состоит из формы, которая принимает имя пользователя / адрес электронной почты и пароль, передавая значения в действие login_attempt .

<% @page_title = «UserAuth | Login» -%>
<div class= «Sign_Form«>
<h1>Log in</h1>
<%= form_tag(:action => ‘login_attempt’) do %>
<p>Username or Email:</br> <%= text_field_tag(:username_or_email) %></p>
<p>Password:</br> <%= password_field_tag :login_password %></p>
<%= submit_tag(«Log In») %>
<% end %>
</div>

view raw
login.html.erb
hosted with ❤ by GitHub


Создание действия `login_attempt`

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

class SessionsController < ApplicationController
def login
#Login Form
end
def login_attempt
authorized_user = User.authenticate(params[:username_or_email],params[:login_password])
if authorized_user
flash[:notice] = «Wow Welcome again, you logged in as #{authorized_user.username}«
redirect_to(:action => ‘home’)
else
flash[:notice] = «Invalid Username or Password»
flash[:color]= «invalid»
render «login»
end
end
end

Здесь чего-то не хватает. Нам нужно сохранить вошедшее в систему состояние пользователя. Если мы этого не сделаем, нам придется проверять подлинность перед каждым запрошенным действием, которое не является СУХИМЫМ и может быть дорогостоящим. Мы будем использовать сеанс для отслеживания состояния пользователя, проверяя его перед каждым запросом пользователя.

Посмотрим, как это сделать.


Печенье, сессии и супер-печенье

Печенье:

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

Использование куки в Rails совсем несложно:

view raw
cookie.rb
hosted with ❤ by GitHub

Сессии:

Однако из-за некоторых ограничений файлов cookie, таких как максимальный размер 4 КБ и тот факт, что они могут быть прочитаны, изменены или удалены, мы будем использовать сеансы. Используя сеансы, веб-сервер отправляет идентификатор в файле cookie в браузер. Идентификатор отправляется обратно при каждом запросе и используется для извлечения данных из сеанса, которые хранятся на сервере.

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

session[:name]= «Azzurrio»

view raw
session.rb
hosted with ❤ by GitHub

Супер-Cookie:

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

Конфигурация сеанса: внутри папки config / initializers вы найдете два файла конфигурации для сеансов. Первый — session_store.rb , который используется для настройки опции хранения, которую вы хотите использовать ( cookie_store — опция по умолчанию). Второй — secret_token.rb , который содержит строку, которую Rails использует для шифрования файла cookie.

Теперь вернемся к нашему приложению и сохраним состояние входа в сеанс, если пользователь авторизован. Вернуться в действие login_attempt

def login_attempt
authorized_user = User.authenticate(params[:username_or_email],params[:login_password])
if authorized_user
session[:user_id] = authorized_user.id
flash[:notice] = «Wow Welcome again, you logged in as #{authorized_user.username}«
redirect_to(:action => ‘home’)
else
flash[:notice] = «Invalid Username or Password»
flash[:color]= «invalid»
render «login»
end
end

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

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

«Всегда храните идентификатор, который ссылается на объект в файле сеанса, а не на сам объект»


Ограничение доступа

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

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

Метод before_filter принимает имя метода, выполняемого перед действием. Второй параметр — это список действий, которые мы хотим отфильтровать. Давайте добавим это в наше приложение.

В ApplicationController, который является суперклассом всех наших контроллеров, добавьте:

protected
def authenticate_user
if session[:user_id]
# set current user object to @current_user object variable
@current_user = User.find session[:user_id]
return true
else
redirect_to(:controller => ‘sessions’, :action => ‘login’)
return false
end
end
def save_login_state
if session[:user_id]
redirect_to(:controller => ‘sessions’, :action => ‘home’)
return false
else
return true
end
end

Метод authenticate_user проверяет, доступен ли сеанс user_id . Если это так, он назначает пользовательский объект переменной экземпляра @current_user и возвращает true, позволяя выполнить действие. Если нет, верните false и перенаправьте на страницу входа. Другой метод, называемый save_login_state , предотвращает доступ пользователя к страницам регистрации и входа в систему во время входа в систему.

Добавьте вызовы before_filter в SessionsController

before_filter :authenticate_user, :only => [:home, :profile, :setting]
before_filter :save_login_state, :only => [:login, :login_attempt]

и в UsersController

before_filter :save_login_state, :only => [:new, :create]

Теперь значение @current_user можно использовать в шаблонах для представления информации о зарегистрированном пользователе.

<h2 class=’User_Header> <%=@current_user.username%> Profile <h2>


Выйти

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

def logout
session[:user_id] = nil
redirect_to :action => ‘login’
end


Конфигурация маршрутов

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

root :to => «sessions#login»
match «signup», :to => «users#new»
match «login», :to => «sessions#login»
match «logout», :to => «sessions#logout»
match «home», :to => «sessions#home»
match «profile», :to => «sessions#profile»
match «setting», :to => «sessions#setting»

view raw
routes.rb
hosted with ❤ by GitHub

Теперь запустите сервер, войдите в систему, перейдите к другим действиям и выйдите из системы. Довольно мило, а?


Последние мысли

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

Давайте кратко рассмотрим наиболее распространенные библиотеки:

  • Devise : Devise — самая распространенная библиотека в Rails, используемая для аутентификации с рейтингом популярности 24,874 согласно веб сайту ruby-toolbox . Это гибкое решение для аутентификации на основе Warden. Это основанное на стойке и полное решение MVC.
  • Authlogic : Authlogic представляет новый тип модели, позволяющий использовать несколько моделей с проверкой подлинности (в отличие от только модели пользователя). Это чистое и простое решение для проверки подлинности ruby, занимающее второе место в рейтинге популярности с 12.815 .
  • OmniAuth : OmniAuth — это основанная на провайдере структура аутентификации для веб-приложений. Это мощный и гибкий инструмент, позволяющий разработчику делать как можно меньше, используя другие механизмы аутентификации (например, Facebook или Twitter). Его рейтинг популярности — 11,128.

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


Вывод:

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

Спасибо за чтение, я надеюсь, что это было полезно и приятно.