本文约 4,857 字,预计阅读 13 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:要么不敢给权限,要么一给就是管理员
把站点交给客户维护时,权限永远是个难题。给管理员,客户可能误删插件、改坏主题;给编辑,客户又抱怨”我传不了图””我看不到产品菜单””我改不了菜单”。
WordPress 默认的五个角色(管理员、编辑、作者、投稿者、订阅者)是粗粒度的,它假设站点是博客。一旦用了自定义文章类型、自定义字段、SEO 插件、表单插件,这些预设角色就会处处不对:编辑看不到产品的字段,作者看不见自己文章的表单数据。
解决办法是把权限拆到”能力(capability)”这一层,按岗位重新组合。User Role Editor 和 Members 都是做这件事的:前者是”给现有角色增删能力”的工具,后者更偏”重新定义角色体系并支持按内容授权”。两者都基于同一套 WordPress 核心机制。
原理分析:角色、能力与元能力
WordPress 的权限模型是三层:
- 能力(Capability):最小单位,例如
edit_posts、publish_posts、upload_files、manage_options。插件注册自定义文章类型时,会顺带注册一批自己的能力(edit_products、publish_products等,取决于capability_type参数)。 - 角色(Role):能力的集合。一个角色就是一串”能力 => true/false”。用户被赋予角色,从而继承能力。
- 元能力(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」:
- 右上角下拉选目标角色(如 Editor);
- 中间是能力清单,按分组排列(
Core、Post type、Taxonomy、Plugins等),勾选或取消; - 界面右上角有「Show capabilities in human readable form」,把
edit_others_posts显示成中文说明,方便核对; - 点「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 );
常见踩坑
- 给
manage_options就等于给了管理员。 很多 SEO 插件、表单插件把设置页挂在manage_options上,一旦给出去,客户能看到全部插件设置。 add_role()之后改代码不生效。 角色已存在于数据库,再次add_role()会直接返回 null。开发期用remove_role()兜底,或用add_cap()增量更新。- 改完权限自己把自己锁在门外。 先把
manage_options从当前账号所在角色拿掉再保存,就再也进不去了。改动前确认自己有第二个管理员账号,或准备好wp user set-role 1 administrator这条命令。 - 忘了清用户缓存。 能力存在用户 meta 里并被缓存,代码改完前台可能不生效:
bash
wp cache flush
- 自定义文章类型没注册
capability_type。 默认capability_type => 'post'意味着产品沿用edit_posts,无法单独控制。想分开就用'capability_type' => 'product'并配map_meta_cap => true(参见 Custom Post Type UI 创建自定义文章类型)。 unfiltered_html别轻易给。 投稿者级别以上若拿到它,就可以插入<script>,等于给了一个存储型 XSS 入口。- 案例: 某站给”运营”角色勾了
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 内容审核流程 的协作设计。
