一句话答案:WordPress 的权限体系不是「角色」在起作用,而是「能力(Capability)」在起作用——角色只是能力的一个打包套餐。正确的做法是给用户分配角色、在代码里只判断能力(current_user_can()),需要细分时再自定义角色或用 add_cap() / remove_cap() 增删单项能力。
很多人把 WordPress 的权限理解成「管理员 / 编辑 / 作者」这几档固定选项,于是遇到「让编辑能改菜单但不能装插件」这种需求就卡住,要么直接给管理员,要么装一堆权限插件。实际上 WordPress 自带的角色与能力机制足够覆盖绝大多数场景,代价只是你要理解三个概念:角色、能力、元能力。
角色和能力的关系:一个是套餐,一个是单项
官方文档的定义很明确:WordPress 把角色和能力存在 wp_options 表的 user_roles 键里;角色定义了一组能力的集合,能力才真正决定用户能看什么、做什么。默认有六个角色:
| 角色 | slug | 典型能力 | 适用人群 |
|---|---|---|---|
| 超级管理员 | (仅多站点) | 站点网络全部能力 | 网络所有者 |
| 管理员 | administrator | 单站内全部管理功能 | 站点负责人 |
| 编辑 | editor | 发布并管理文章,含他人文章 | 内容主编 |
| 作者 | author | 发布并管理自己的文章 | 专栏作者 |
| 贡献者 | contributor | 撰写和管理自己的文章,不能发布 | 投稿人 |
| 订阅者 | subscriber | 仅 read,管理个人资料 | 注册用户 |
关键点:角色之间没有「高级 / 低级」之分,它们只是职责划分。订阅者只有 read,不是因为它「级别低」,而是因为它只负责读。
自定义角色:add_role() 有一个必须知道的幂等陷阱
新增角色用 add_role(),它接收角色 slug 、显示名和能力数组:
function wporg_simple_role() {
add_role(
'simple_role',
'简单角色',
array(
'read' => true,
'edit_posts' => true,
'upload_files' => true,
)
);
}
add_action( 'init', 'wporg_simple_role' );
坑在这里:第一次调用 add_role() 之后,角色和能力就写进数据库了,后续调用什么都不会发生——包括你后来改了能力数组。这就是为什么很多人改了代码却发现后台权限没变。
要批量改能力,正确姿势是先 remove_role() 再 add_role(),并且必须加条件判断,否则每次请求都读写数据库,性能会明显下降:
function wporg_sync_role() {
$role = get_role( 'simple_role' );
if ( $role && ! $role->has_cap( 'edit_others_posts' ) ) {
remove_role( 'simple_role' );
add_role( 'simple_role', '简单角色', array(
'read' => true,
'edit_posts' => true,
'edit_others_posts' => true,
'upload_files' => true,
) );
}
}
add_action( 'init', 'wporg_sync_role' );
只想微调?用 add_cap() / remove_cap()
如果只是在现有角色上加减一两项能力,不必重建角色:拿到角色对象再操作即可,挂钩优先级要排在角色定义之后。
function wporg_add_caps() {
$role = get_role( 'editor' );
if ( $role ) {
$role->add_cap( 'edit_theme_options' );
$role->remove_cap( 'manage_categories' );
}
}
add_action( 'init', 'wporg_add_caps', 11 );
代码里判断权限:只查能力,别查角色
判断权限有两个常用函数:
current_user_can( $cap, ...$args )—— 判断当前用户user_can( $user, $cap, ...$args )—— 判断指定用户,第三个参数可传对象 ID 做针对性校验
官方文档明确提示:用角色名代替能力去做判断虽然「部分可用」,但结果不可靠,属于不推荐做法。原因很直白——角色可以被插件改,能力才是最终裁决依据。
元能力:为什么 edit_post 要传文章 ID
能力分两类。edit_posts 这类是「原始能力」,角色直接拥有;edit_post、edit_user、edit_post_meta 这类是「元能力」,会通过 map_meta_cap() 映射成原始能力再校验。
current_user_can( 'edit_posts' ); // 原始能力,直接查
current_user_can( 'edit_post', $post->ID ); // 元能力,需带对象 ID
current_user_can( 'edit_post_meta', $post->ID, $meta_key );
同一个 edit_post,作者写自己的文章映射成 edit_posts,编辑改别人的文章映射成 edit_others_posts。不传 ID 就无从判断,这也是权限代码最常见的 bug 来源。
为什么超级管理员永远返回 true?
实战:搭一个「内容运营」角色
- 明确职责边界:能写能发能传图、能改菜单和站点设置,不能装插件换主题、不能动用户。
- 用 add_role() 建 content_operator 角色,能力给 read 、 edit_posts 、 edit_others_posts 、 publish_posts 、 edit_published_posts 、 upload_files 、 manage_categories 、 edit_theme_options 。
- 在主题和插件的敏感入口统一用 current_user_can( ‘activate_plugins’ ) 之类的能力判断,不写角色名。
- 用 user_can( $user_id, ‘edit_post’, $post_id ) 写一段校验脚本,抽查该角色对他人文章的编辑权限是否真的生效。
- 后台手动把测试账号切到该角色,逐项点一遍后台菜单,确认没有残留入口。
REST API 时代的权限排查:401 和 403 怎么看
区块编辑器、站点编辑器都是走 REST API 读写的,权限问题会以接口报错的形式暴露出来:
- 模板、模板部件、导航等端点校验
edit_theme_options - 文章、页面类端点校验
edit_posts、edit_pages、edit_others_posts - 缺失时返回 401 Unauthorized 或 403 Forbidden,响应体里通常带具体失败的能力名
典型症状:自定义角色能打开站点编辑器,但某些区块保存失败。排查方法就是打开浏览器开发者工具,看哪个请求返回 401/403,再对照响应里的错误码补对应能力。这一步比猜要快得多。
unfiltered_html 的特殊规则
unfiltered_html 允许用户在文章、页面、评论、小工具里写入未过滤的 HTML 甚至 JavaScript 。官方明确警告:给不可信用户开这个能力等于开后门。在 WordPress 多站点下,只有超级管理员拥有 unfiltered_html,单站点管理员默认拥有。
权限设计的三条实践建议
- 最小权限原则:只给完成职责必需的能力。一次多给,后面很难收回。
- 能力优先于角色:所有代码判断写
current_user_can(),角色只用于后台分配。 - 自定义能力要有命名空间:比如
jinyu_manage_billing,避免和核心或插件能力撞名。自定义能力在默认后台不产生任何效果,但可以在自定义后台页面和前台逻辑里用——这正是它的价值。
权限这件事,做对的标志不是「功能都能用」,而是「不该出现的东西一个都没出现」。角色只是入口,能力才是闸门。
改了 add_role() 的能力数组,为什么后台没变化?
current_user_can('edit_post') 为什么判断不准?
可以给某个用户单独加能力吗?
自定义角色打开站点编辑器后部分功能报错怎么办?
相关阅读
- WordPress 自定义文章类型(CPT)完整教程——CPT 可以声明自己专属的能力集,和本文的角色机制配套使用
- WordPress 自定义字段与元数据怎么用——涉及 edit_post_meta 元能力的实际场景
- 多作者 WordPress 站点怎么用 AI 管内容协作——把角色分工落到真实的内容生产流程里
- 从零写第一个 WordPress 插件——在插件里注册自定义能力与菜单权限






评论 (0)