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

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

  • Во-первых, существует ли пользователь? - аутентификация
  • Во-вторых, есть ли у пользователя логин? - авторизация
  • В-третьих, каковы разрешения? - авторизация
Другие вопросы по тегам