(Примечание. Исходный код, сопровождающий эту статью, можно найти здесь .)
Сегодня мы узнаем, как реализовать простую аутентификацию пользователей в приложении Rails с нуля. Мы рассмотрим лучшие практики, чтобы помочь избежать распространенных, часто дорогостоящих ошибок.
Введение в аутентификацию пользователей
Действия, защищенные паролем, являются распространенной функцией в большинстве веб-приложений, позволяя только зарегистрированным пользователям вводить действительный пароль. Это называется «Аутентификация пользователя», и это требуется многим приложениям Rails. Давайте начнем с быстрого сценария того, как работает процесс аутентификации пользователя.
- Регистрация: создайте нового пользователя. Этот пользователь собирается зарегистрироваться с помощью имени пользователя, пароля (который будет зашифрован в базе данных), электронной почты и т. Д.
- Логин: позволяет пользователю войти в систему с его / ее действительным именем пользователя и паролем. Процесс аутентификации происходит путем сопоставления имени пользователя и пароля в базе данных, что позволяет пользователю получить доступ к защищенным действиям только в том случае, если данная информация успешно соответствует записанным значениям. Если нет, пользователь снова будет перенаправлен на страницу входа.
- Ограничение доступа: создайте сеанс для хранения идентифицированного идентификатора пользователя после входа в систему, чтобы можно было легко перемещаться по дополнительным защищенным действиям, просто проверяя идентификатор пользователя в текущем сеансе.
- Выход из системы: разрешите пользователю выйти и установить идентифицированный идентификатор пользователя в файле сеанса на ноль.
Создать пользовательскую модель и контроллер
Сначала давайте создадим наше приложение с именем User_Auth . Мы используем MySQL в качестве нашей базы данных:
$ rails new User_Auth -d mysql |
Перейдите в каталог приложения в вашем терминале и сгенерируйте Модель пользователя и Контроллер. Контроллер получит новый метод в качестве параметра.
$ rails cd ./User_Auth | |
$ rails g model user | |
$ rails g controller users new |
Настройте базу данных
Теперь нам нужно создать таблицу Users в базе данных. Как вы, наверное, знаете, это всего лишь простая миграция.
class CreateUsers < ActiveRecord::Migration | |
def change | |
create_table :users do |t| | |
t.string :username | |
t.string :email | |
t.string :encrypted_password | |
t.string :salt | |
t.timestamps | |
end | |
end | |
end |
Мы добавили 4 столбца в таблицу Users (имя пользователя, адрес электронной почты, encrypted_password & salt). Помните, что мы никогда не храним пароли в виде простого текста, всегда сначала шифруем значение перед сохранением его в базе данных. Существуют различные типы методов шифрования, которые мы рассмотрим позже в этом руководстве.
«Мы никогда не храним пароли в виде простого текста, его всегда нужно сначала зашифровать, прежде чем сохранять в базе данных».
После успешного создания модели и миграции создайте базу данных и перенесите ее для создания таблицы Users .
$ rake db:create | |
$ rake db:migrate |
Создание нового пользовательского действия
Далее напишем новые и создадим действия в UsersController.
class UsersController < ApplicationController | |
def new | |
@user = User.new | |
end | |
def create | |
@user = User.new(params[:user]) | |
if @user.save | |
flash[:notice] = «You signed up successfully» | |
flash[:color]= «valid» | |
else | |
flash[:notice] = «Form is invalid» | |
flash[:color]= «invalid» | |
end | |
render «new» | |
end | |
end |
Мы создали два основных действия:
- new : просто создайте новый пользовательский объект, который будет отображаться в новом шаблоне (мы скоро это увидим). Шаблон включает в себя форму регистрации. Данные в этой форме будут отправлены в действие создания , описанное далее …
- create : создает пользователя на основе параметров, переданных из нового шаблона, и сохраняет его в базе данных. Если пользователь успешно создан, перенаправьте его туда, куда вы захотите, после действия регистрации. Здесь мы помещаем «Вы успешно зарегистрировались» во флэш-хэш, чтобы указать успешность, в противном случае визуализируйте новый шаблон снова.
Шаблон формы регистрации
Перед написанием формы регистрации я создал простой макет в файле application.html.erb внутри каталога views / layout.
Теперь давайте напишем форму регистрации внутри нового шаблона.
<% @page_title = «UserAuth | Signup» %> | |
<div class=»Sign_Form«> | |
<h1>Sign Up</h1> | |
<%= form_for(:user, :url => {:controller => ‘users’, :action => ‘create’}) do |f| %> | |
<p> Username:</br> <%= f.text_field :username%> </p> | |
<p> Email:</br> <%= f.text_field :email%> </p> | |
<p> Password:</br> <%= f.password_field :password%></p> | |
<p> Password Confirmation:</br> <%= f.password_field :password_confirmation%> </p> | |
<%= f.submit :Signup %> | |
<% end %> | |
<% if @user.errors.any? %> | |
<ul class=»Signup_Errors«> | |
<% for message_error in @user.errors.full_messages %> | |
<li>* <%= message_error %></li> | |
<% end %> | |
</ul> | |
<% end %> | |
</div> |
Здесь мы создали форму для регистрации, которая берет имя пользователя, адрес электронной почты, пароль и пароль подтверждения от пользователя. Эти значения будут отправлены как params [: user] в действие create . Оператор if проверяет наличие ошибок в случае, если пользователь вводит неверные данные.
Вы также, возможно, заметили, что форма регистрации имеет два поля: пароль и подтверждение_парафа, которые должны соответствовать пользователю в базе данных. Мы должны добавить методы attr_accessor в пользовательскую модель для их обработки.
Добавление некоторых проверок в модель пользователя
В дополнение к attr_accessors нам нужно добавить несколько правил проверки, чтобы убедиться, что входные данные соответствуют нашим требованиям.
class User < ActiveRecord::Base | |
attr_accessor :password | |
EMAIL_REGEX = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i | |
validates :username, :presence => true, :uniqueness => true, :length => { :in => 3..20 } | |
validates :email, :presence => true, :uniqueness => true, :format => EMAIL_REGEX | |
validates :password, :confirmation => true #password_confirmation attr | |
validates_length_of :password, :in => 6..20, :on => :create | |
end |
Мы создали страницу регистрации, которая создает нового пользователя и проверяет входные данные, но мы не шифровали пароль. Прежде чем мы это сделаем, давайте поговорим о шифровании пароля.
Методы шифрования паролей
Как упоминалось ранее, никогда не храните пароли в базе данных в виде простого текста. Шифрование паролей является наилучшей практикой и является фундаментальным для всех подходов аутентификации пользователей в Rails.
Хеширование пароля
Хеширование — это процесс применения математических функций и алгоритмов к строке данных для получения уникальной выходной строки. При создании нового пользователя простой текстовый пароль хешируется и сохраняется в базе данных. Когда пользователь входит в систему, входной пароль хешируется и сравнивается с хешированным паролем, хранящимся в базе данных. Этот метод называется односторонним шифрованием, то есть одни и те же входы с одной и той же функцией хеширования всегда будут давать один и тот же результат.
Мы можем реализовать метод хеширования, используя SHA1 в Rails, используя всего две строки кода
require ‘digest/sha1’ | |
encrypted_password= Digest::SHA1.hexdigest(password) |
Солевой пароль
Из-за некоторых недостатков, которые существуют в технике хеширования паролей, таких как таблицы Rainbow , использование пароля является более безопасным способом шифрования паролей. «Соль» — это дополнительная строка данных, добавляемая к паролю перед его шифрованием. Он должен быть уникальным и случайным, чтобы сделать недостаток таблиц Rainbow бесполезным.
Шифрование с использованием соли:
salt= Digest::SHA1.hexdigest(«# We add {email} as unique value and #{Time.now} as random value») | |
encrypted_password= Digest::SHA1.hexdigest(«Adding #{salt} to {password}») |
Bcrypt
Есть другой простой способ зашифровать пароли, а не создавать соль с нуля: используйте bcrypt. bcrypt-ruby — это гем ruby для шифрования, и мы будем использовать его в нашем приложении.
Чтобы установить его, просто добавьте в файл gem следующее:
gem ‘bcrypt-ruby’, :require => ‘bcrypt’ |
и внутри вашей директории приложения запустите:
$ bundle install |
тогда вы можете просто написать:
salt = BCrypt::Engine.generate_salt | |
encrypted_password = BCrypt::Engine.hash_secret(password, salt) |
Callbacks
Нам нужны две функции: одна для шифрования действительного пароля (в виде простого текста) перед сохранением пользовательской записи, а другая функция для назначения пароля attr_accessor для nil. Поскольку мы уже зашифровали и сохранили пароль в базе данных, мы больше не будем его использовать, и мы можем сделать это с помощью обратных вызовов before_save и after_save .
Теперь давайте добавим эти функции и обратные вызовы в модель пользователя.
before_save :encrypt_password | |
after_save :clear_password | |
def encrypt_password | |
if password.present? | |
self.salt = BCrypt::Engine.generate_salt | |
self.encrypted_password= BCrypt::Engine.hash_secret(password, salt) | |
end | |
end | |
def clear_password | |
self.password = nil | |
end |
Защита массовых назначений
Одна из наиболее распространенных проблем безопасности в Rails называется «уязвимость массового назначения», и она проистекает из соглашения ActiveRecord о создании методов получения и установки для всех значений атрибутов объекта ActiveRecord.
В действии создания вместо непосредственного назначения каждого атрибута по одному мы используем хэш всех значений, которые мы хотим присвоить атрибутам субъекта. Это вышеупомянутое массовое задание и суть проблемы. Есть атрибуты, которые мы не хотим, чтобы пользователь мог изменять через форму, но мы не проверяем параметры.
Чтобы избежать этого, в Rails есть два метода для защиты атрибутов от массового назначения.
- attr_protected: все атрибуты, отмеченные как attr_protected , игнорируются при массовом назначении, и все остальные атрибуты будут доступны.
- attr_accessible: все атрибуты marekd с attr_accessible доступны во время массового назначения, а все остальные атрибуты будут защищены.
Наконец, давайте добавим доступные атрибуты в модель пользователя
attr_accessible :username, :email, :password, :password_confirmation |
Это завершает процесс регистрации! Вы можете запустить свой сервер, зарегистрироваться как новый пользователь и протестировать его, чтобы убедиться, что пароль успешно зашифрован в базе данных!
Примечание. Не забудьте добавить маршрут по умолчанию в файл маршрутов! Вы можете просто откомментировать в конце файла.
match ‘:controller(/:action(/:id))(.:format)’ |
Еще больше делать
Еще предстоит еще много работы, прежде чем мы сможем сказать, что процесс аутентификации пользователя завершен. В моем следующем посте я расскажу о работе с сессиями и файлами cookie, ограничении доступа и настройке маршрута. Спасибо за прочтение!