Авторизация в приложении на Ruby on Rails с помощью гема six

Александр Борисов, 17 августа 2014

Задачу авторизации в приложении на Ruby on Rails решают различные гемы: cancan, cancancan, protector. Я хочу поделиться решением для авторизации на основе очень простого гема six.

Основные задачи авторизации в приложении

Как правило, у нас в приложении есть текущий пользователь – гость или известный нам пользователь – и мы хотим понять две вещи: какие блоки на странице можно показывать текущему пользователю, и какие действия в контроллере текущий пользователь может выполнять.

Чтобы проверить, можно ли показывать тот или иной блок на странице текущему пользователю, удобно использовать условный оператор с методом can?:

Для того, чтобы определить, может ли текущий пользователь выполнить запрошенное действие в контроллере удобнее всего использовать явный метод-проверку authorize!, который поднимет исключение в случае, если доступ запрещён. Это исключение можно отлавливать и перенаправлять пользователя на страницу авторизации с сообщением об ошибке доступа, например.

Как работает гем six

Чтобы проверить, есть ли у текущего пользователя доступ к действию над ресурсом, нужно выполнить три шага:

А вообще, крайне рекомендую почитать исходный код six, там всего около 200 строк с подробными комментариями. Помимо того, что вы разберётесь с тем, как работает этот гем, вы обязательно почерпнёте что-нибудь для себя по теме написания хорошего кода.

Используем six в приложении на Ruby on Rails

Теперь нам необходимо реализовать методы can?, authorize!, и понять, каким образом мы будем привязывать список доступных действий к объектам в нашем приложении.

Реализация can? и authorize!

Логичнее всего реализовывать методы проверки доступа в контроллерах, так как в модели MVC, которую использует Ruby On Rails, именно контроллеры отвечают за те действия, которые нужно выполнить в ответ на запрос пользователя. При этом метод can? мы разрешим использовать в вьюшках с помощью helper_method.

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

Метод store_location сохраняет в сессии адрес последнего выполненного запроса для последующего использования в redirect_back_or_default. Сам store_location вызывается с помощью хука after_action на любой успешно выполненный GET-запрос пользователя на получение HTML.

Теперь мы можем использовать наш concern в контроллере:

В строке 6 мы ловим исключение AccessDenied и обрабатываем его с помощью метода access_denied_handler, в котором показываем сообщение о неудачной попытке доступа и отправляем пользователя на последнюю удачно запрошенную страницу с помощью redirect_back_or_default.

В строке 11 параметр subject имеет значение по умолчанию для использования метода can? в виде <%= render 'cart' if can? :buy %> во вьюшках – в этом случае поиск действия :buy будет происходить в списке, который вернёт метод allowed для current_user.

В методе current_user в строке 21 в случае, если пользователь не вводил логина и пароля, в качестве текущего пользователя используется объект класса Guest. Это позволяет указывать список доступных действий для неавторизованного пользователя также, как и для авторизованного.

Теперь, чтобы использовать методы can? и authorize! на сайте, достаточно наследовать соответствующие контроллеры от app/controllers/web/application_controller.rb.

В контроллере админки реализация будет отличаться только использованием current_admin_user вместо current_user, Admin::Guest вместо Guest, и другой обработкой неудачного доступа к ресурсу:

Привязка списка доступных действий к объектам в приложении

В каждом объекте, для которого мы проверяем список доступных действий, нам нужно реализовать метод allowed, который будет возвращать список этих самых действий для текущего пользователя.

Метод allowed вынесен в отдельный concern из-за того, что определение набора возможных действий над пользователем не относится напрямую к обязанностям класса User. Сам concern лучше всего положить lib/allowed/user_rules.rb – это соглашение позволяет быстро находить списки действий, относящиеся к тем или иным объектам приложении.

В строке 8 мы с помощью метода kind_of? мы проверяем класс субъекта на предмет того, является ли сам класс субъекта или один из его классов-предков классом User. Если не является, то возращаем пустой набор возможных действий.

В строках с 10 по 18 мы добавляем действия в набор возможных действий в зависимости от объекта – текущего пользователя.

Преимущества авторизации в RoR-приложении на основе six

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

Смотрите также


comments powered by Disqus
Блог Цифрономики

Мысли о веб-разработке на Ruby on Rails: работа с кодом, приёмы, инструменты, организация процесса разработки.

@cifronomika
RSS


Веб-разработка на Ruby on Rails, реализация сложных проектов
mailbox@cifronomika.ru
+7 (910) 535-99-11