Аутентификация является одним из ключевых элементов многих веб-приложений. Это каменная стена между приложением и его пользователями, поэтому важно, чтобы подход к аутентификации был безопасным и простым в использовании. Но что это за «аутентификация»? Это способ гарантировать, что только пользователи, авторизованные нашим приложением, могут использовать приложение. Как я уверен, вы знаете, что существует много способов аутентификации пользователя, таких как электронная почта / пароль, OpenID Connect, SAML, SSO и так далее.
Сегодня мы рассмотрим другой подход: аутентификация без пароля.
Что такое аутентификация без пароля?
Когда пользователь регистрируется на веб-сайте, приложение позволяет ему выбирать свои учетные данные, обычно имя пользователя или адрес электронной почты / пароль. Затем пользователь может в любое время ввести эти учетные данные для входа в приложение. Аутентификация без пароля в основном исключает часть пароля и использует только электронную почту для регистрации и входа в систему.
Как это работает, когда пользователь регистрируется в нашем приложении, отправляется электронное письмо для активации его учетной записи. Это позволяет нам проверить, принадлежит ли электронная почта пользователю. Теперь, когда у нас есть подтвержденное электронное письмо, в следующий раз, когда пользователь попытается войти в систему, мы отправим ему электронное письмо с токеном, который пользователь сможет использовать для входа в приложение. Как только пользователь нажимает на ссылку с токеном, приложение аутентифицирует пользователя.
Давайте начнем. Инициализируйте ваше приложение рельсов:
$ rails new passwordless $ cd passwordless
Чтобы получить более ясное представление о том, что на самом деле происходит, мы не будем использовать какие-либо библиотеки или гемы для этого урока.
Создание модели
Начнем с создания модели, необходимой для нашего приложения. Мы будем называть эту модель User
поскольку это пользователи, которых мы аутентифицируем. Но вы можете использовать все, что работает.
$ rails g scaffold user fullname username:uniq email:uniq login_token token_generated_at:datetime $ rails db:create && rails db:migrate
fullname
username
и username
являются обязательными. Мы добавили username
чтобы пользователи могли войти по email
или через username
. У нас также есть пара других столбцов, login_token
и token_generated_at
, которые в основном являются одноразовыми паролями, которые мы генерируем для наших пользователей, а также когда они были сгенерированы.
На уровне таблицы есть уникальное ограничение, но давайте также добавим валидации ActiveRecord для модели. Добавьте следующее в app / models / user.rb :
validates :email, :username, uniqueness: true, presence: true
Наряду с этим, давайте также добавим фильтр before для форматирования имени username
и email
перед сохранением записи. Эти проверки также должны быть в клиенте, но это дополнительная мера. Добавьте фильтр перед:
... before_save :format_email_username def format_email_username self.email = self.email.delete(' ').downcase self.username = self.username.delete(' ').downcase end
Здесь мы в основном убираем пробелы в username
и email
и делаем его строчными, прежде чем сохранить его в базе данных.
Поскольку мы собираемся разрешить пользователям входить в систему по имени пользователя и электронной почте, давайте добавим вспомогательный метод, который извлекает запись пользователя на основе либо:
... def self.find_user_by(value) where(["username = :value OR email = :value", {value: value}]).first end
Постановка на учет
Когда модель готова и готова, давайте продолжим и создадим файл нашего контроллера и необходимые маршруты. Прежде чем приступить к созданию контроллера регистрации, быстро создайте статическую страницу для отображения сообщений:
$ rails g controller static home
Добавьте приведенный ниже маршрут в config / rout.rb :
root 'static#home'
Наряду с этим, также добавьте следующую строку в ваш app / views / layouts / application.html.erb , который используется для отображения сообщений пользователю:
... <body> <p id="notice"><%= notice %></p> ...
Создайте пользовательский контроллер:
$ rails g controller users
В config / rout.Rb добавьте следующие маршруты, которые мы будем использовать для регистрации пользователей:
resources :users, only: [:create] get '/register', to: 'users#register'
Теперь давайте добавим код в app / controllers / users_controller.rb, который соответствует маршрутам, объявленным выше:
def register @user = User.new end def create @user = User.new(user_params) if @user.save redirect_to root_path, notice: 'Welcome! We have sent you the link to login to our app' else render :register end end private def user_params params.require(:user).permit(:fullname, :username, :email) end
Теперь создайте файл представления для регистрации в app / views / users / register.html.erb и добавьте в него эту форму:
<h1>Register</h1> <%= form_for(@user) do |f| %> <% if @user.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@user.errors.count, "error") %> prohibited this @user from being saved:</h2> <ul> <% @user.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= f.label :fullname %> <%= f.text_field :fullname %> </div> <div class="field"> <%= f.label :username %> <%= f.text_field :username %> </div> <div class="field"> <%= f.label :email %> <%= f.text_field :email %> </div> <div class="actions"> <%= f.submit 'Register' %> </div> <% end %>
Отмечая особенное в этой форме. Это стандартная Rails, сгенерированная скаффолом форма, которая фиксирует fullname
username
, username
и email
для пользователя и отправляет его в нашу конечную точку create
. Запустите сервер Rails и зайдите /register
и увидите, что регистрация активна!
Ссылка для входа
Давайте перейдем к содержательной части приложения: отправка электронных писем с логином. По сути, когда новый пользователь регистрируется или когда он запрашивает вход в систему, мы должны отправить ему ссылку для входа с токеном. Когда ссылка нажата, приложение войдет в систему пользователя. Начните с добавления следующих вспомогательных методов в наш apps / models / user.rb для отправки электронных писем:
... def send_login_link generate_login_token template = 'login_link' UserMailer.send(template).deliver_now end def generate_login_token self.login_token = generate_token self.token_generated_at = Time.now.utc save! end def login_link "http://localhost:3000/auth?token=#{self.login_token}" end def login_token_expired? Time.now.utc > (self.token_generated_at + token_validity) end def expire_token! self.login_token = nil save! end private def generate_token SecureRandom.hex(10) end def token_validity 2.hours end end
Пожалуйста, не зацикливайтесь на том, где живет код, который отправляет почту. Я стараюсь максимально снизить уровень шума и сосредоточиться на концепциях более высокого уровня. Я бы никогда не
UserMailer
, например, в модель, но это только для демонстрационных целей.
Итак, у нас есть метод send_login_link
который мы вскоре будем использовать, чтобы отправить ссылку для входа в систему для пользователя. Прежде чем сохранить его в нашей базе данных, мы фактически хешируем его, используя BCrypt, что делает его более безопасным в случае взлома данных. Наряду с этим также добавьте гем ‘bcrypt’ в ваш Gemfile.
Как только мы сгенерируем токен для входа, отправьте его пользователю по электронной почте с помощью ActionMailer UserMailer
. Настройка почтовых функций в этом учебном пособии пропущена, поскольку существует множество хороших учебных пособий, в которых объясняется, как их выполнять в соответствии с вашим поставщиком электронной почты. Просто убедитесь, что вы включили аргумент ссылки, который мы передаем UserMailer в метод `send_login_link`, в шаблон электронной почты, который вы отправляете пользователю.
login_link
настроена с помощью URL- login_link
localhost
, но измените его соответственно для своего приложения. Кроме того, длительность token_validity
установлена на 2 часа, но вы можете изменить ее, очевидно. Наконец, добавьте эту строку в app / controllers / users_controller.rb create
action сразу после строки @user.save
:
... if @user.save @user.send_login_link ...
Теперь, когда у нас есть необходимые вспомогательные методы, давайте добавим маршрут получения, чтобы обработать ссылку для входа в систему, которую мы отправляем в электронном письме.
Контроллер сессий
Начните с создания контроллера для сессии.
$ rails g controller session auth
Обновите файл config / rout.Rb , изменив get 'session/auth'
на get '/auth/:user_id/:token', to: 'session#auth'
. В сгенерированный файл session_controller.rb добавьте этот код:
def auth token = params[:token].to_s user_id = params[:user_id] user = User.find_by(id: user_id) if !user || !user.valid_token? redirect_to root_path, notice: 'It seems your link is invalid. Try requesting for a new login link' elsif user.login_token_expired? redirect_to root_path, notice: 'Your login link has been expired. Try requesting for a new login link.' else sign_in_user(user) redirect_to root_path, notice: 'You have been signed in!' end end
Используя вспомогательный метод, проверьте, является ли токен действительным или срок его действия истек. Если это не верно, перенаправьте на домашнюю страницу с соответствующими сообщениями. Есть вспомогательный метод, который мы использовали, sign_in_user
, который мы должны создать. Откройте app / controllers / application_controller.rb и добавьте:
def sign_in_user(user) user.expire_token! session[:email] = user.email end def current_user User.find_by(email: session[:email]) end
Мы в основном истекаем токен пользователя и сохраняем электронную почту пользователя в сеансе. Также мы добавили вспомогательный метод для извлечения пользователя из сеанса. Функция без пароля готова, продолжайте и попробуйте зарегистрироваться для нового пользователя для проверки функциональности входа в систему.
Авторизоваться
В качестве последнего шага мы будем использовать наши вспомогательные методы для входа пользователя. Начните с добавления этих маршрутов в файл config / rout.rb :
resources :session, only: [:new, :create]
и добавьте приведенный ниже код в файл /app/controllers/session_controller.rb :
def new end def create value = params[:value].to_s user = User.find_user_by(value) if !user redirect_to new_session_path, notice: "Uh oh! We couldn't find the username / email. Please try again." else user.send_login_link redirect_to root_path, notice: 'We have sent you the link to login to our app' end end
Мы только что использовали send_login_link
чтобы сделать тяжелую работу. Последний элемент приложения — форма входа в систему. Создайте файл app / views / session / new.html.erb и добавьте следующую форму:
<%= form_tag "/session" do %> <label> Email / Username </label> <%= text_field_tag "value" %> <%= submit_tag "Login" %> <% end %>
Это простая форма, которая делает работу за нас.
Вывод
С этим мы пришли к завершению урока. В наши дни логин без пароля действительно набирает обороты и предоставляет нашим пользователям менее отвлекающий и более удобный способ аутентификации. О, и это также обеспечивает меньше накладных расходов для всех участников. Я рекомендую вам попробовать подход для входа без пароля для вашего приложения, по крайней мере, в качестве дополнительного метода входа в систему. Это было бы началом!
Весь пример кода, используемый в этом руководстве, доступен на Github , не стесняйтесь раскошелиться и поиграть с ним. Я благодарю вас за чтение учебника и надеюсь, что он послужил вашим целям.