搜索全站
插件 13 分钟阅读

User Role Editor 与 Members 精细控制权限:自定义角色与能力

给客户开后台又怕他改坏?WordPress 的角色权限可以按能力逐项分配。本文讲清角色与能力模型,给出 User Role Editor 与 Members 的配置要点,以及用 add_role、map_meta_cap 写进代码的工程化做法。

U
UFO_PF 2021-04-06 · 阅读 13 分钟
Us 插件
阅读提示
本文约 4,857 字,预计阅读 13 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:要么不敢给权限,要么一给就是管理员

把站点交给客户维护时,权限永远是个难题。给管理员,客户可能误删插件、改坏主题;给编辑,客户又抱怨”我传不了图””我看不到产品菜单””我改不了菜单”。

WordPress 默认的五个角色(管理员、编辑、作者、投稿者、订阅者)是粗粒度的,它假设站点是博客。一旦用了自定义文章类型、自定义字段、SEO 插件、表单插件,这些预设角色就会处处不对:编辑看不到产品的字段,作者看不见自己文章的表单数据。

解决办法是把权限拆到”能力(capability)”这一层,按岗位重新组合。User Role Editor 和 Members 都是做这件事的:前者是”给现有角色增删能力”的工具,后者更偏”重新定义角色体系并支持按内容授权”。两者都基于同一套 WordPress 核心机制。

原理分析:角色、能力与元能力

WordPress 的权限模型是三层:

  1. 能力(Capability):最小单位,例如 edit_posts、publish_posts、upload_files、manage_options。插件注册自定义文章类型时,会顺带注册一批自己的能力(edit_products、publish_products 等,取决于 capability_type 参数)。
  2. 角色(Role):能力的集合。一个角色就是一串”能力 => true/false”。用户被赋予角色,从而继承能力。
  3. 元能力(Meta Capability):edit_post、delete_post、read_post 这类不能直接授予、必须结合具体对象判断的能力。核心通过 map_meta_cap() 把它们映射成 edit_posts、edit_others_posts、edit_published_posts 这类原始能力的组合。

这个模型意味着:

  • 给用户加权限,本质是给角色加能力;
  • 判断代码一律用 current_user_can( 'xxx' ),不要去读角色名(角色名可以任意改,能力才是契约);
  • 想做”只能编辑自己的文章”,靠的是 edit_others_posts 这个能力有没有,而不是在代码里写 if 判断作者。

几个必须知道的高危能力:

能力 风险
manage_options 等同于全站设置,接近管理员
unfiltered_html 可插入任意 HTML/JS,XSS 风险
edit_theme_options 可改主题设置、菜单、小工具
activate_plugins 可启用/停用插件
edit_users / promote_users 可改他人权限,能把自己提权
edit_files 可在后台直接编辑主题/插件源码

保姆级实操

第一步:盘点现有角色缺什么

先别急着改。用 WP-CLI 看清楚:

# 列出角色
wp role list

# 看某个角色具体有哪些能力
wp role get editor --fields=name,capabilities --format=json

再让客户实际操作一遍,记录每一处”点不了/看不到”,逐条对应到能力上。多数问题来自三类:upload_files(传图)、自定义文章类型的 edit_products 系列、manage_categories(分类维护)。

第二步:用 User Role Editor 增删能力

后台「Users → User Role Editor」:

  1. 右上角下拉选目标角色(如 Editor);
  2. 中间是能力清单,按分组排列(Core、Post type、Taxonomy、Plugins 等),勾选或取消;
  3. 界面右上角有「Show capabilities in human readable form」,把 edit_others_posts 显示成中文说明,方便核对;
  4. 点「Update」保存,立即生效。

常见组合(”网站运营”这个岗位):

read
upload_files
edit_posts / edit_published_posts / delete_posts / delete_published_posts
publish_posts
edit_pages / edit_published_pages / publish_pages / delete_published_pages
manage_categories
edit_products / edit_published_products / publish_products

第三步:用 Members 建立新角色体系

Members 的思路是”角色也进后台 UI”:「Members → Roles → Add New Role」,填角色名(shop_operator)与显示名,然后在能力清单里勾选。Members 的特点:

  • 支持新增/删除/克隆角色,也能给单个用户叠加角色(把多个角色分配给同一用户);
  • 有 Capabilities 界面,可以把插件注册的自定义能力登记进来统一管理;
  • 带 Content Permissions 组件,可以针对单篇文章设置”哪些角色能看”,适合做会员内容。

第四步:把角色定义写进代码(团队协作推荐)

界面改的权限不会进 Git。工程化做法是写在功能插件里,只在激活时执行一次:

// 注册"客服"角色,仅首次生效
register_activation_hook( __FILE__, function () {
    add_role( 'support_agent', '客服', array(
        'read'                    => true,
        'upload_files'            => true,
        'edit_posts'              => true,
        'edit_published_posts'    => true,
        'delete_posts'            => false,
        'publish_posts'           => false,
        'manage_tickets'          => true,   // 自定义能力
    ) );
} );

register_deactivation_hook( __FILE__, function () {
    remove_role( 'support_agent' );
} );

// 给管理员补上自定义能力(否则管理员也可能没有)
add_action( 'admin_init', function () {
    $admin = get_role( 'administrator' );
    if ( $admin && ! $admin->has_cap( 'manage_tickets' ) ) {
        $admin->add_cap( 'manage_tickets' );
    }
} );

注意 add_role() 写的是数据库记录,改能力后不会覆盖已有角色,需要先用 remove_role() 再 add_role(),或直接用 add_cap() / remove_cap() 增量调整。

第五步:用元能力做细粒度约束

“客服只能看自己名下的工单”这类需求,用 map_meta_cap 实现:

add_filter( 'map_meta_cap', function ( $caps, $cap, $user_id, $args ) {
    if ( 'edit_post' !== $cap || empty( $args[0] ) ) {
        return $caps;
    }
    $post = get_post( $args[0] );
    if ( $post && 'ticket' === $post->post_type ) {
        // 非管理员只能改自己创建的工单
        if ( ! user_can( $user_id, 'manage_options' ) && (int) $post->post_author !== (int) $user_id ) {
            $caps[] = 'do_not_allow';
        }
    }
    return $caps;
}, 10, 4 );

常见踩坑

  1. 给 manage_options 就等于给了管理员。 很多 SEO 插件、表单插件把设置页挂在 manage_options 上,一旦给出去,客户能看到全部插件设置。
  2. add_role() 之后改代码不生效。 角色已存在于数据库,再次 add_role() 会直接返回 null。开发期用 remove_role() 兜底,或用 add_cap() 增量更新。
  3. 改完权限自己把自己锁在门外。 先把 manage_options 从当前账号所在角色拿掉再保存,就再也进不去了。改动前确认自己有第二个管理员账号,或准备好 wp user set-role 1 administrator 这条命令。
  4. 忘了清用户缓存。 能力存在用户 meta 里并被缓存,代码改完前台可能不生效:

bash
wp cache flush

  1. 自定义文章类型没注册 capability_type。 默认 capability_type => 'post' 意味着产品沿用 edit_posts,无法单独控制。想分开就用 'capability_type' => 'product' 并配 map_meta_cap => true(参见 Custom Post Type UI 创建自定义文章类型)。
  2. unfiltered_html 别轻易给。 投稿者级别以上若拿到它,就可以插入 <script>,等于给了一个存储型 XSS 入口。
  3. 案例: 某站给”运营”角色勾了 edit_users,理由是”他需要改作者信息”。两个月后发现运营账号被钓鱼邮件拿走,攻击者用它把自己提权成了管理员,装上后门插件。复盘结论:edit_users + promote_users 属于”提权能力”,任何非管理员角色都绝不能给;改作者这种需求应该由管理员做,或者做成专门的元框 + 自定义能力控制。判断标准:如果某个能力能让持有者获得比现在更高的权限,它就不该下发。

总结

权限设计按这三步走:先按岗位拆出”能力清单”并用 WP-CLI 核实现状,再用 User Role Editor(改现有角色)或 Members(建新角色体系)落地,最后把稳定的部分用 add_role() / add_cap() 写进代码。

红线三条:绝不给非管理员 manage_options、unfiltered_html、edit_users/promote_users;改权限前确保自己有第二个管理员账号与 wp user set-role 的补救手段;改完清缓存并让目标角色实际操作验证。后台可见性还可以配合 Adminimize 精简后台界面 做二次收口,内容审核流程则可参照 PublishPress 内容审核流程 的协作设计。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。