role_core: https://github.com/rails-engine/role_core
cancancan: https://github.com/CanCanCommunity/cancancan
先说效果,后台有个Roles,是后台用户的角色。
AdminUser就是后台用户,可分配某些角色。用户只能操作角色中赋予的功能
安装gem
# 后台界面
gem 'activeadmin'
gem 'devise'
# 用户访问权限
gem 'cancancan', '~> 2.0'
# 用户访问角色权限配置
gem 'role_core'
activeadmin在之前的配置在之前的文章中讲过
运行命令,生成abilitiy的文件
rails g cancan:ability
配置ActiveAdmin的权限管理适配器为cancancan,并且指定验证失败后的回调函数 access_denied
config.authorization_adapter = ActiveAdmin::CanCanAdapter
config.on_unauthorized_access = :access_denied
在application_controller中配置access_denied函数
class ApplicationController < ActionController::Base
protect_from_forgery
# cancanadapter,访问被拒绝后会回调这个方法
def access_denied(exception)
redirect_to admin_root_path, alert: exception.message
end
# cancanadapter,访问被拒绝后会回调这个方法,上面的那个方法无效,提示多了个参数(暂不知原因)
def access_denied
redirect_to admin_root_path, alert: "您没有权限访问!"
end
这里先只能访问Dashboard这个页面
class Ability
include CanCan::Ability
def initialize(user)
can :read, ActiveAdmin::Page, name: "Dashboard", namespace_name: "admin"
end
end
到这里,cancancan和ActiveAdmin的集成已经完毕。至于权限代码还是得自己去实现。
现在通过集成role_core来制定用户角色权限,之后就可以让运营自己玩了
根据role_core的配置文档来运行命令
图片.png
这边是定义用户和角色是多对多的关系:
图片.png
role_core结合cancancan,在initializeres/role_core打开注释:
require "role_core/contrib/can_can_can_permission"
RoleCore.permission_class = RoleCore::CanCanCanPermission
并且继续定义各个model的操作权限
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.
# For I18n, see `config/locales/role_core.en.yml` for details which followed the rule of ActiveRecord's I18n,
# See <http://guides.rubyonrails.org/i18n.html#translations-for-active-record-models>.
# Uncomment below if you want to integrate with CanCanCan
#
require "role_core/contrib/can_can_can_permission"
RoleCore.permission_class = RoleCore::CanCanCanPermission
RoleCore.permission_set_class.draw do
# Define permissions for the application. For example:
#
# permission :foo, default: true # `default: true` means grant to user by default
# permission :bar
#
# You can also group permissions by using `group`:
#
# group :project do
# permission :create
# permission :destroy
# permission :update
# permission :read
# permission :read_public
#
# # `group` supports nesting
# group :task do
# permission :create
# permission :destroy
# permission :update
# permission :read
# end
# end
#
# For CanCanCan integration, you can pass `model_name` for `group` or `permission`. For example:
#
# group :project, model_name: "Project" do
# permission :create
# permission :destroy, model_name: 'Plan'
# end
#
# That will translate to CanCanCan's abilities (if user has these permissions),
# the permission's name will be the action:
#
# can :create, Project
# can :destroy, Plan
#
# You can pass `_priority` argument to `permission`
#
# group :project, model_name: "Project" do
# permission :read_public,
# permission :read, _priority: 1
# end
#
# That will made 'read' prior than `read_public`.
#
# For CanCanCan's hash of conditions
# (see https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities#hash-of-conditions)
# you can simply pass them as arguments for `permission` even with a block
#
# group :task, model_name: "Task" do
# permission :read_public, is_public: true
# permission :update_my_own, action: :update, default: true do |user, task|
# task.user_id == user.id
# end
# end
#
# Although permission's name will be CanCanCan's action by default,
# you can pass `action` argument to override it.
#
# permission :read_public, action: :read, is_public: true
#
# For some reason, you won't interpret the permission to CanCanCan,
# you can set `_callable: false` to `permission` or `group`
#
# permission :read, _callable: false
#
group :admin do
# group :user, model_name: "User" do
# permission :create
# permission :destroy
# permission :update
# permission :read
# permission :manage
# end
#
# group :admin_user, model_name: "AdminUser" do
# permission :create
# permission :destroy
# permission :update
# permission :read
# permission :manage
# end
# 这里是简便的写法
%w(user admin_user role).each do |item|
group item.to_sym, model_name: "#{item.camelcase}" do
permission :create
permission :destroy
permission :update
permission :read
permission :manage
end
end
end
end.finalize! # Call `finalize!` to freezing the definition, that's optional.
在AdminUser中加入验证权限代码 computed_permissions
class AdminUser < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable,
:recoverable, :rememberable, :validatable
# AdminUser与Role多对多
has_many :role_assignments, dependent: :destroy
has_many :roles, through: :role_assignments
def computed_permissions
roles.map(&:computed_permissions).reduce(RoleCore::ComputedPermissions.new, &:concat)
end
end
在ability.rb中加入配置user.computed_permissions.call(self, user)
class Ability
include CanCan::Ability
def initialize(user)
user.computed_permissions.call(self, user)
can :read, ActiveAdmin::Page, name: "Dashboard", namespace_name: "admin"
end
end
ActiveAdmin中创建或更新的model和视图
admin_users.rb
ActiveAdmin.register AdminUser do
permit_params :email, :password, :password_confirmation, role_ids: []
index do
selectable_column
id_column
column :email
column :current_sign_in_at
column :sign_in_count
column :created_at
actions
end
filter :email
filter :current_sign_in_at
filter :sign_in_count
filter :created_at
form partial: 'form'
end
admin_users/_form.html.erb
<%= form_for([:admin, @admin_user], local: true) do |f| %>
<% if @admin_user.errors.any? %>
<article class="message is-danger">
<div class="message-header">
<p>
<%= pluralize(@admin_user.errors.count, "error") %> prohibited this form from being saved:
</p>
</div>
<div class="message-body">
<% @admin_user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</div>
</article>
<% end %>
<div class="field">
<%= f.label :email, class: 'label' %>
<p class="control">
<%= f.text_field :email, id: :admin_user_email, class: 'input' %>
</p>
</div>
<% if @admin_user.persisted? %>
<div class="field">
<%= f.label :roles, class: 'label' %>
<p class="control">
<%= f.collection_check_boxes :role_ids, Role.all, :id, :name do |m| %>
<%= m.label class: 'checkbox' do %>
<%= m.check_box class: 'checkbox' %>
<%= m.text %>
<% end %>
<% end %>
</p>
</div>
<% end %>
<div class="field is-grouped">
<p class="control">
<%= f.submit class: 'button is-primary' %>
</p>
<p class="control">
<%= link_to 'Back', url_for(:back), class: 'button is-link' %>
</p>
</div>
<% end %>
role.rb
ActiveAdmin.register Role do
form partial: 'form'
# 注意:要写这个,不然创建不成功
controller do
def permitted_params
params.permit!
end
end
end
roles/_form.html.erb
<%= form_for([:admin, @role], local: true) do |f| %>
<% if @role.errors.any? %>
<article class="message is-danger">
<div class="message-header">
<p>
<%= pluralize(@role.errors.count, "error") %> prohibited this form from being saved:
</p>
</div>
<div class="message-body">
<% @role.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</div>
</article>
<% end %>
<div class="field">
<%= f.label :name, class: 'label' %>
<p class="control">
<%= f.text_field :name, id: :role_name, class: 'input', required: 'required' %>
</p>
</div>
<%= render partial: "permissions", locals: {f: f, name: :permissions_attributes, permissions: @role.permissions} %>
<div class="field is-grouped">
<%= f.submit class: 'button is-primary' %>
<%= link_to 'Back', url_for(:back), class: 'button is-link' %>
</div>
<% end %>
roles/_permissions.html.erb
<%= f.fields_for name, permissions do |ff| %>
<% if permissions.class.attribute_names_for_inlining.any? %>
<div class="field">
<label class="label">
<%= permissions.class.model_name.human %>
</label>
<p class="control">
<% permissions.class.attribute_names_for_inlining.each do |permission| %>
<%= ff.label permission, class: 'checkbox' do %>
<%= ff.check_box permission, class: 'checkbox' %>
<%= permissions.class.human_attribute_name(permission) %>
<% end %>
<% end %>
</p>
</div>
<% end %>
<% permissions.class.attribute_names_for_nesting.each do |permission| %>
<%= render partial: "permissions", locals: {f: ff, name: permission, permissions: permissions.send(permission)} %>
<% end %>
<% end %>
网友评论