Ruby on Rails 4: авторизация после аутентификации с несколькими моделями Devise
Я создал простое приложение Ruby on Rails 4 с несколькими простыми моделями и базой данных PosgreSQL, которая развернута на моем тестовом VPS. Затем я создал три модели Devise с использованием генераторов Rails.
Я выбрал отдельные модели Devise, поскольку модели полностью отличаются друг от друга. Метод наследования одной таблицы не подлежал сомнению.
Я понимаю, что с любой моделью Devise я могу выполнять аутентификацию на месте, регистрировать пользователей, разработанных и т. Д. Это работает.
Теперь я планирую создать место, где будут проходить авторизацию. Я отказался от CanCan, так как в настоящий момент он не поддерживается Rails 4 в соответствии с тем, что я нашел с помощью Google.
Поэтому наиболее подходящим вариантом, который я нашел, было просто использование before_filter
и пользовательский метод аутентификации, который в свою очередь проверяет current_user
Тип или его существование и возвращается, если это хорошо, чтобы пойти или нет.
Вот пример псевдокода, который я уже пробовал, и похоже, что он работает.
before_filter :custom_authentication!
def custom_authentication!
if current_admin
redirect_to "admin_page"
elsif current_monkey
redirect_to "zoo"
elsif current_human
redirect_to "home"
else
# some unauthorised access alert
end
end
Насколько я понимаю, мне нужно поместить этот вид кода в каждый контроллер, который есть в моем приложении на Rails 4. Это верно?
Мой главный вопрос: есть ли лучший способ сделать это? Я думал о введении одного метода в application_controller.rb
это создает полную логику авторизации для каждого типа пользовательской модели с поддержкой Devise, которая когда-либо обращалась к контроллерам моего приложения. Это было бы лучшее место для этой логики? Это правильный подход? Или мне нужно перезагрузить компьютер и использовать другой подход?
2 ответа
Я столкнулся с той же проблемой, пытаясь найти решение для авторизации пользователей после того, как обнаружил, что CanCan не поддерживается в Rails 4. Я выбрал вариант Pundit. Это действительно гибкий и работает очень хорошо.
https://github.com/elabs/pundit
По сути, вы настроили несколько классов политик, которые управляют авторизацией пользователей для действий вашего контроллера.
У posts_controller будет класс политики, подобный этому:
class PostPolicy
attr_reader :user, :post
def initialize(user, post)
@user = user
@post = post
end
def update?
user.admin?
end
end
и вы бы авторизовали действие обновления в контроллере:
def update
@post = Post.find(params[:id])
authorize @post
if @post.update(post_params)
redirect_to @post
else
render :edit
end
end
Так что, если пользователь не является администратором, он выдаст ошибку. Pundit позволяет вам восстановить отказавшую авторизацию в ApplicationController и добавить свой собственный код. Так, например, вы можете сделать что-то вроде этого:
class ApplicationController < ActionController::Base
protect_from_forgery
include Pundit
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
private
def user_not_authorized
if current_monkey
redirect_to "zoo"
elsif current_human
redirect_to "home"
else
flash[:error] = "You are not authorized to perform this action."
redirect_to request.headers["Referer"] || root_path
end
end
end
Вы также можете настроить политики для нескольких пользователей devise в одном классе Policy. Например, в классе PostPolicy вы можете проверить тип пользователя и предоставить права на основании этого:
def index?
return true if user.type == "Monkey"
end
Как сказал miahabdu, сначала аутентификация (см., Если пользователь существует), затем авторизация (см. Разрешения пользователя).
Тем не менее, вам не нужно прекращать использовать устройство, и при этом вам не нужен Cancan.
Вот простой способ сделать "Авторизацию" сразу после аутентификации.
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action :authenticate_user!
before_action :check_access
private
def check_access
if current_user.present?
unless find_permission(current_user, 'code assigned for login access')
sign_out current_user
redirect_to new_user_session_path
end
end
end
end
Не зацикливайтесь на чем find_permission
является. В принципе current_user
от разработки, и sign_out current_user
делает именно это. Но сразу после того, как вы подпишете current_user
вне, вам нужно redirect_to new_user_session_path
предполагая User
Модель - это то, что вы используете для разработки.
Теперь единственное небольшое изменение, которое я бы сделал в вашем коде, - это использование CaseElse вместо IfElse.
Итак, чтобы реализовать ваш код в моем примере, я бы сделал это для check_access
метод:
def check_access
if current_user.present?
unless find_permission(current_user, 'code assigned for login access')
sign_out current_user
redirect_to new_user_session_path
end
if current_admin
redirect_to "admin_page"
elsif current_monkey
redirect_to "zoo"
elsif current_human
redirect_to "home"
else
sign_out current_user
redirect_to new_user_session_path
end
end
end
Наконец, зачем проверять доступ к выходу из системы в начале? Это быстрее. Вам не придется тратить системное время на проверку всех разрешений, когда, в конце концов, пользователю все равно не разрешается входить в систему.
- Во-первых, существует ли пользователь? - аутентификация
- Во-вторых, есть ли у пользователя логин? - авторизация
- В-третьих, каковы разрешения? - авторизация