我创建了一个简单的Ruby on Rails 4应用程序,其中包含一些简单模型和部署在我的测试VPS上的PosgreSQL数据库。然后我使用Rails生成器创建了三个Devise模型。
我选择了单独的Devise模型,因为模型彼此完全不同。 single table inheritance方法毫无疑问。
据我所知,对于任何Devise模型,我都可以在网站上执行身份验证,注册“设计”用户等。这些东西可行。
现在我计划建立一个地方来进行授权。我放弃了CanCan,因为根据我在Google上发现的内容,目前Rails 4不支持。
所以我找到的最合适的选项只是使用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
中放置一个方法,为每个访问我的app控制器的Devise用户模型构建整个授权逻辑。对于这种逻辑,这会是一个更好的地方吗?这是正确的方法吗?或者我是否需要重新启动并使用其他方法?
答案 0 :(得分:1)
我在Rails 4中发现CanCan不支持后试图找到用户授权解决方案时遇到了同样的问题。我选择使用的选项是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
您还可以在同一Policy类中为多个设计用户配置策略。例如,在PostPolicy类中,您可以检查用户的类型并根据该权限授予权限:
def index?
return true if user.type == "Monkey"
end
答案 1 :(得分:0)
像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
最后,为什么要在开始时检查注销权限?它更快。您不必花费系统时间检查所有权限,但最终还是不允许用户登录。