1

我一直在此站点上的 Devise RDocs、Google 中搜索我的问题的答案,但没有运气。

假设我有四个 Devise 范围/角色,每个都有自己的属性、登录页面和单独的 Web 流:

  • 学生们
  • 教授
  • 院长
  • 院系

所有这些都使用 User 类并具有以下共同属性。

  • ID
  • 姓名
  • 电子邮件
  • 密码
  • 角色

这是我为设置它而建立的路线示例:

devise_for :students, :class_name => 'User'
devise_for :professors, :class_name => 'User'
devise_for :deans, :class_name => 'User'
devise_for :faculties, :class_name => 'User'
devise_for :users

然后我生成了设计范围的视图并使用了这些视图。

之后,我必须在我的应用程序控制器中添加一些代码来覆盖 Devise::RegistrationsController,它希望将所有内容路由到根路径:

def after_sign_in_path_for(resource)
 user_role = resource.role
 case user_role
   when "professor"
     professors_url
   when "faculty"
     faculties_url
   when "dean" 
     deans_url
   when "student"
     students_url
   else
     root_path       
  end 
end

def after_sign_out_path_for(resource)
  case resource
   when :faculty
     new_faculty_session_path
   when :professor
    new_professor_session_path
   when :dean 
     new_dean_session_path
   when :student
     new_student_session_path
   else
     root_path       
  end     
end

我可以使用出色的助手,例如签到?它告诉我是否有任何上述范围内的用户登录。太好了!!!现在我需要current_user的类似功能。

我可以访问以下助手。

  • 现在的学生
  • 当前教授
  • current_dean
  • current_faculty

它们工作得很好,但这是我遇到问题的地方。假设我有一个共享所有这些范围的视图。现在,如果我在该视图上尝试current_student并且我以教授身份登录,它将无法正常工作。

例如:我想在每个页面上包含一个部分,以允许用户在登录后注销。这就是我为学生做的事情。工作得很好。

<% if student_signed_in? %>
 <div style="float: right;">Welcome <%= current_student.name %></div>
 <div>
    <%= link_to('Logout', destroy_student_session_path, :method => :delete) %>        
 </div>
<% end %>

我想要做的是这样的事情,它将提供注销我所有范围/资源的能力,无论我是以学生、院长、教授还是教职员工身份登录的:

<% if signed_in? %>
 <div style="float: right;">Welcome <%= current_resource.name %></div>
 <div>
    <%= link_to('Logout', destroy_resource_session_path, :method => :delete) %>        
 </div>
<% end %>

我的下一步是添加我自己的辅助方法来确定范围,如下所示:

def current_resource
     current_professor unless current_professor.nil?
     current_student unless current_student.nil?
     current_dean unless current_dean.nil?
     current_faculty unless current_faculty.nil?
end

def destroy_resource_session_path
     destroy_professor_session_path unless current_professor.nil?
     destroy_student_session_path unless current_student.nil?
     destroy_dean_session_path unless current_dean.nil?
     destroy_faculty unless current_faculty.nil?
end

如果这种方法行得通,我会这样做,但如果我决定在未来添加更多角色或通用功能,这似乎非常低效和乏味......

一定有更好的方法?这让我觉得我在错误地使用设计,或者只是在某个地方遗漏了一些东西。

4

3 回答 3

3

经过更多研究后,我发现了一个名为 CanTango 的宝石,它完全符合我的要求。它适用于设计并处理多个用户模型类型。看一看。

https://github.com/kristianmandrup/cantango/wiki/Cantango-with-devise-accounts

谢谢你们回答我的问题。我从你的回答中学到了很多。

于 2011-10-20T04:41:43.457 回答
0

你从 Ryan bates 那里检查过CanCan吗?它允许您仅使用设计用户模型,并根据您的用户模型中的角色定义能力。然后,它使您可以访问辅助方法can?cannot?进一步定义您的能力。

编辑:
我会将属性移动到引用模型,并使用 CanCan 引导 Web 流。您在网络上看到的示例并不总是显示 CanCan 将为您提供的全部功能。您可以将链接放在您的登录页面上,以将他们引导到相应的登录页面,每个登录页面都具有相同的设计登录表单。保持逻辑after_sign_in_path_for,并使用 CanCan 锁定受限操作。这简化了您的路线以及整体应用程序逻辑和设计。此外,仅使用一种模型,故障排除设计错误就容易了 75%。

于 2011-10-17T00:43:51.070 回答
0

根据对上述答案的评论,另一种方法是使用多个 rails 应用程序(每个角色一个),并且在它们之间共享相同的数据库(请参阅SO 上的这个问题),或者制作另一个应用程序仅用于身份验证和授权,并通过它管理所有登录。

如果您不想使用一个应用程序,上述解决方案实际上只是一种后备方案。我建议只是继续做你现在正在做的事情,确定范围并制定可以处理每种情况的方法。

使用精心设计的方法,您可以做得更好。例如,修改destroy_resource_session_path以接受一个参数,current_user分配给current_professor, ... current_faculty不为零的任何一个,然后调用destroy_#{current_user}_session_path。要进一步详细说明如何执行此操作:

您不需要在应用程序中明确需要明确定义的角色,更多的是只需将每个角色路由到一组不同的模型/视图/控制器,因此最好的方法是制作一个单独的控制器来确定角色并路由请求根据用户角色分配给适当的控制器。

编辑:

一般来说,我认为你的做法是正确的,但如果你将处理不同用户的方法(比如你拥有的 2 个)复制到一个单独的控制器中destroy_resource_session_path,并带有 和 等操作,你可以做得更好current_resource。同样,将after_sign_in_path_forandafter_sign_out_path_for方法添加到该新控制器

关键是新控制器应该处理用户类型之间的差异,因此添加新类型的用户就像在该控制器中添加几行一样简单,然后修改routes.rb.

只需将您拥有的一角色存储在该控制器中即可。然后,您可以修改您的方法以使用该集合来确定存在哪些角色,因此您所要做的就是向集合添加一个条目并routes.rb添加一个新角色的条目。例如,您将修改after_sign_in_path_for(resource)为:

def after_sign_in_path_for(resource)
  user_role = resource.role
  if (set_of_roles.include?(user_role))
    return #{user_role}_url
  else
    return root_path
  end
end

请注意,这#{..}是在 ruby​​ 中插入字符串的方式,因此#{user_role}_url在 ruby​​ 中等同于user_role+"_url"在 Java 中。

使用这样的方法,添加新角色唯一要做的就是为该角色创建一个新视图,定义一个名为role_namein的路由routes.rb,并将角色添加到set_of_roles.

我建议在使用之前研究单点登录的想法——我不熟悉它,但我在 SO 上链接到的问题的答案说它使事情变得过于复杂。

于 2011-10-17T02:46:36.513 回答