EP098. “Subscriber 笔记权限与服务端强制私密”
🔒 登录后可标记已读给 Subscriber 角色开放创建/编辑/删除自己笔记的权限(复用 EP087 学过的 capability_type + Members 插件角色授权套路),并且解决一个真实的隐私漏洞:note 文章类型开了 show_in_rest,意味着任何人不登录也能访问 REST API 直接读到全站所有用户的笔记内容。解法分两层:先在 WordPress 后台把文章状态设成 private(私有内容默认不出现在公开查询/API 里);但客户端传的 status 值完全不可信(恶意用户可以改自己浏览器里的 JS 把 status 硬改成 publish),所以还要在服务端用 wp_insert_post_data 过滤器强制所有 note 类型文章的状态改成 private,客户端传什么都不算数。
涉及文件
wp-content/mu-plugins/university-post-types.php(修改)- 后台 Members → Roles:给 Administrator、Subscriber 授权 Notes 权限(后台操作,无对应代码文件)
wp-content/themes/fictional-university-theme/functions.php(修改)
代码实现
mu-plugins/university-post-types.php:给 note 文章类型加独立权限(跟 EP087 处理 event/campus 是同一套路):
// wp-content/mu-plugins/university-post-types.php
// note 文章类型注册参数里新增:
'capability_type' => 'note',
'map_meta_cap' => true,
后台授权:Members → Roles → Administrator,勾选 Notes Tab 下的全部权限;Subscriber 角色只勾选这 5 项:publish_notes、edit_notes、edit_published_notes、delete_notes、delete_published_notes——不勾选 edit_others_notes / delete_others_notes,确保 Subscriber 只能动自己的笔记。
functions.php:服务端强制所有 note 文章的状态为 private(追加在文件末尾):
// wp-content/themes/fictional-university-theme/functions.php
// Force note posts to be private
add_filter('wp_insert_post_data', 'makeNotePrivate');
function makeNotePrivate($data) {
if($data['post_type'] == 'note' AND $data['post_status'] != 'trash') {
$data['post_status'] = "private";
}
return $data;
}
关键改动点:
- Subscriber 只授予 5 个权限、刻意不给
edit_others_notes/delete_others_notes——因为默认要「零信任」对待 Subscriber 这类开放注册就能拿到的角色,只给完成功能所需的最小权限集合 edit_notes/delete_notes管的是「草稿」状态的笔记,edit_published_notes/delete_published_notes管的是「已发布」状态的笔记——note 的笔记后面会被强制成private状态而不是draft,但权限体系里private内容依然要满足edit_published_*这条权限才能改,所以两组权限都要给,缺一个都会导致某些操作失败wp_insert_post_data是一个在文章即将写入数据库前触发的过滤器,收到的$data是即将保存的全部字段(标题、内容、状态等),函数把它改完再return,改完的版本才会真正写进数据库——这是比在前端信任 JS 传来的值更可靠的做法,因为浏览器端的任何代码理论上都能被访问者篡改,只有服务端的检查才靠得住- 判断条件必须排除
post_status == 'trash'这个例外——如果不排除,会导致「删除笔记」操作失效:删除笔记本质上是把状态改成trash,如果这个过滤器把任何 note 的状态都强制改回private,那么永远没法把它挪进回收站 - 这个过滤器对所有文章类型的保存都会触发,所以必须先判断
$data['post_type'] == 'note',不然会连博客文章、Program、Professor 这些原本该公开的文章类型也被一起强制成私有
[截图:wp-admin Members 插件 Roles 设置页面,Subscriber 角色勾选的 Notes 相关权限(publish_notes/edit_notes/edit_published_notes/delete_notes/delete_published_notes)]
Hook / Function 速查
| 名称 | 类型 | 用途 |
|---|---|---|
wp_insert_post_data | WP filter hook | 文章即将写入数据库前触发,可以在这里拦截并修改即将保存的数据 |
常见坑
- 只在前端 JavaScript 里把
status设成private就以为万事大吉——客户端的任何值都可能被篡改,隐私/权限相关的强制规则必须在服务端兜底,不能只依赖前端传的数据 wp_insert_post_data过滤器忘记排除post_status == 'trash'的情况——会导致这个文章类型永远没法被删除(挪进回收站的动作会被强制改回 private)- 只给 Subscriber 授予
edit_notes而不给edit_published_notes——note 笔记很快会变成private状态,只给「草稿」权限的话用户会发现自己没法编辑已保存的笔记
延伸 / 后续讲座会用到
下一讲(part 2)会继续深挖安全相关的细节。
Sources
Udemy:
- Become a WordPress Developer: Unlocking Power With Code — Section 19, EP098