本篇博客介绍后台用户角色分配,以及权限控制的策略。
  假定一个内部用户只能拥有一个角色,简单灵活的管理策略,即为每一位用户分配一个角色,为该角色设置访问权限。
  为了达到管理上的灵活性,权限管理的粒度,细分到每一个 Controller 的每一个 Action。
  最小化设计需要四个模型:用户(User)、角色(Role)、用户角色分配(UserRole)、角色权限控制(Permission)。
  详细模型和字段设计,以及测试数据如下:
  
1. 用户(User)模型 +----+-------+ | id | name | +----+-------+ | 1 | Henry | | 2 | Alice | | 3 | Davie | +----+-------+ 2. 角色(Role)模型 +----+--------------+----------------------+ | id | name | description | +----+--------------+----------------------+ | 1 | 管理员 | 后台管理员 | | 2 | 客服 | 客服人员 | | 3 | 商务合作 | 商务合作--临时 | +----+--------------+----------------------+ 3. 用户角色分配(UserRole)模型 +----+---------+---------+ | id | user_id | role_id | +----+---------+---------+ | 1 | 3 | 3 | | 2 | 2 | 2 | | 3 | 1 | 1 | +----+---------+---------+ 4. 角色权限控制(Permission)模型 +----+---------+-----------------+------------------------+ | id | role_id | controller_name | action_names | +----+---------+-----------------+------------------------+ | 1 | 1 | controllers | all | | 2 | 2 | schools | index;edit;show;update | | 3 | 2 | users | index;edit;show;update | | 4 | 3 | schools | index;show | | 5 | 3 | users | index;show | +----+---------+-----------------+------------------------+
  角色分配界面
   
  权限设置界面
   
1. 通过解析 Rails Route 路由的配置,来实现动态解析所有 Controllers 和 Actions 的设置。
      def admin_routes(refresh=false)
        # TODO use cache, and refresh cache manully
        routes_to_display = Rails.application.routes.routes.collect do |route|
          ActionDispatch::Routing::RouteWrapper.new(route)
        end.reject do |route|
          route.internal?
        end.collect do |route|
          route.reqs if route.reqs.index('admin/')
        end.compact!.uniq!
        collect_routes(routes_to_display)
      end
      def collect_routes(routes)
        ret = {}
        routes.each do |route|
          slash = route.index('/')
          number = route.index('#')
          controller_name = route[slash + 1, number - slash - 1]
          action_name = route[number + 1, route.size]
          if ret.has_key?(controller_name)
            ret[controller_name] << action_name
          else
            ret[controller_name] = [action_name]
          end
        end
        ret
      end
  
  2. 判断用户是否具有访问权限。
  
    def permitted?(user, controller_name, action_name)
      user_role = user.user_role
      if user_role
        permit_controller_action?(user_role.role_id, controller_name, action_name) ||
        permit_all_actions?(user_role.role_id, controller_name) ||
        permit_all_controllers?(user_role.role_id)
      end
    end
  
更多实现的细节代码,以及提交到 Github,项目名: Joydaxue
2014-11-08