Вы когда-нибудь задумывались, есть ли какой-нибудь простой способ ограничить некоторые базовые функции и возможности пользователям, которые соответствуют определенным критериям или чему-то в этом роде? Что ж, не беспокойтесь, вы можете использовать гем CanCanCan, чтобы сделать пользовательский интерфейс вашего приложения rails очень гибким. Используя CanCanCan, мы можем определять роли и разрешения для пользователя, с помощью которых мы можем ограничить опыт определенных типов пользователей, а также дает нам уровень безопасности.
CanCanCan Разрешения и возможности:
Начнем с основ,
Класс Ability содержит все разрешения. Здесь мы определяем все разрешения, которые хотим иметь в нашем приложении. Однако при необходимости мы можем разбить его на несколько классов, мы увидим их позже.
Class Ability include CanCan::Ability def initialize(user) can :manage, Blog end end
Метод can
используется для определения разрешений и требует двух аргументов. Первое - это действие, для которого вы устанавливаете разрешение, второе - это класс объекта, для которого вы его устанавливаете.
can :manage, Blog # user can perform any action on the article can :read, :all # user can read any object can :manage, :all #user has access to everything can :highlight_pages, Blog # custom action can [:add_user, :delete], [Blog, Comment] # multiple actions
Важно знать, как происходит приоритет разрешений, подробнее об этом вы можете узнать здесь.
Используя: all, мы можем представить все классы.
Обычно мы работаем с четырьмя основными действиями, которые мы используем для определения и проверки разрешений. Это не то же самое, что 7 действий RESTful в Rails. CanCanCan автоматически добавляет несколько удобных псевдонимов для сопоставления действий контроллера.
alias_action :index, :show, :to => :read alias_action :new, :to => :create alias_action :edit, :to => :update
Мы также можем передать хэш условий, чтобы еще больше ограничить, к каким записям применяется это разрешение.
can :read, Blog, active: true, user_id: user.id
Для этих условий важно использовать только столбцы базы данных, чтобы ее можно было повторно использовать для Извлечения записей. Здесь active и user_id - столбцы таблицы Blog.
Вы можете использовать вложенные хэши для определения условий для ассоциаций. Здесь Блог можно читать только в том случае, если категория, к которой он принадлежит, относится к жанровому образованию.
can :read, Blog, category: { genre: 'education' }
Если ваши условия слишком сложны для определения в хэше, вы можете использовать блок, чтобы определить их в Ruby.
can :update, Blog do |blog| blog.view_count > 20 end
Использование определенных разрешений:
Теперь, когда мы рассмотрели основы определения разрешений, давайте посмотрим, как использовать их в нашем приложении.
Вы можете получить список записей, доступных текущему пользователю на основе действия.
@blogs = Blog.accessible_by(current_ability, :update)
Обратите внимание, что разрешения, определенные в блоке, не рассматриваются в приведенном выше коде. Вы получаете список записей блога, которые не прошли проверку view_count ›20. Current_ability - это метод, доступный с помощью CanCanCan в ваших контроллерах. Подробнее об этом можно узнать здесь.
После определения разрешений вы можете использовать метод can?
в контроллере или представлении, чтобы проверить разрешение пользователя для данного действия и объекта.
can_manage = can? :manage, @blog
Вы также можете передать класс модели, но если существует блок или хэш условий, они будут проигнорированы при проверке класса, и он вернет true
.
Авторизация контроллеров:
Мы можем использовать команду «authorize!» Для действия контроллера, чтобы авторизовать его.
def custom_action @blog = Blog.find(params[:blog]) authorize! :custom_action, @blog end
Вместо вызова авторизации! при каждом действии контроллера мы можем делать
class BlogsController < ActionController::Base load_and_authorize_resource end
load_and_authorize_resource на самом деле является комбинацией load_resource и authorize_resource
«Load_resource» создаст объект для каждого действия контроллера, которое можно использовать для авторизации, как показано ниже.
@blogs = Blog.accessible_by(current_ability) # collection routes @blog = Blog.find(params[:id]) # member routes
«Authorize_resource» будет разрешать каждое действие контроллера на основе загруженного ресурса.
CanCanCan вызовет CanCan::AccessDenied
исключение, когда пользователь выполнит действие контроллера, на которое у него нет разрешения.
Если вы хотите больше узнать об этом, вы можете заглянуть здесь.
Проблемы с обслуживанием одного файла capacity.rb:
Когда вы только начали создавать свое приложение с нуля, было бы неплохо просто сохранить все разрешения внутри файла capacity.rb, но как только ваше приложение начнет масштабироваться, вы можете начать замечать некоторые проблемы, такие как
- Каждый раз загружаются ненужные разрешения.
- Утомительный процесс определения и изменения разрешений
Модульная обработка файла capacity.rb:
Если ваше приложение будет разрастаться, очень важно преобразовать ваш файл capacity.rb в модули. Мы можем разделить capacity.rb на основе каждой модели, учитывая, что у вас есть две модели, а именно Пользователь и Блог.
ability.rb class Ability include CanCan::Ability def initialize(user) can :manage, :all if user.admin? can :edit, User, id: user.id can :read, Blog, published: true can :edit, Blog, user_id: user.id end end
Мы разделяем права каждой модели на отдельные файлы способностей.
ability.rb class Ability include CanCan::Ability def initialize(user) can :manage, :all if user.admin? end end user_ability.rb class UserAbility include CanCan::Ability def initialize(user) can :manage, Comment, user_id: user.id end end blog_ability.rb class BlogAbility include CanCan::Ability def initialize(user) can :read, Blog, published: true can :edit, Blog, user_id: user.id end end
Теперь мы можем загрузить только необходимые файлы способностей для конкретного контроллера. По умолчанию каждый контроллер просматривает файл capacity.rb для получения разрешений, однако мы можем переопределить это.
blogs_controller.rb class BlogsController def current_ability @current_ability ||= BlogAbility.new(current_user) end end
Обратите внимание, что важно кэшировать объект способности, чтобы он не воссоздавался каждый раз. Когда ваш контроллер запущен, он будет читать только нужный вам файл способностей, экономя время и память.
Вы также можете объединить два файла способностей вместе.
def current_ability @current_ablity ||= UserAbility.new(current_user).merge(Ability.new(current_user)) end
Разрешения в UserAbility будут иметь приоритет над Ability здесь. Хотя согласно официальной документации, будущие версии будут поддерживать это изначально, и переопределение current_ability не потребуется.
Francium Tech - технологическая компания, специализирующаяся на поставке высококачественного масштабируемого программного обеспечения на экстремальных скоростях. Цифры и размер данных нас не пугают. Если у вас есть какие-либо требования или вы хотите бесплатно проверить работоспособность своих систем или архитектуры, напишите письмо по адресу [email protected], мы свяжемся с вами!