EP107. “登录校验、防重复点赞与目标类型校验”
🔒 登录后可标记已读给 createLike() 端点加上三层自定义限制:① 必须登录才能点赞;② 同一用户对同一教授只能点一次;③ 传来的 ID 必须真的是一个 Professor 类型的文章,不能是随便编的数字或别的文章类型的 ID。过程中也顺手修复了一个真实 bug:未登录访客查看教授页面时,心形图标却显示成「已点赞」的实心状态——原因是 get_current_user_id() 在未登录时返回 0,而 WP_Query 里 'author' => 0 会被当成「不限定作者」处理,导致查询条件形同虚设。
涉及文件
wp-content/themes/fictional-university-theme/inc/like-route.php(修改)wp-content/themes/fictional-university-theme/single-professor.php(修改)wp-content/themes/fictional-university-theme/src/modules/Like.js(修改)
代码实现
inc/like-route.php:createLike() 加上登录校验、防重复、目标类型校验三层限制:
// wp-content/themes/fictional-university-theme/inc/like-route.php
function createLike($data) {
if (is_user_logged_in()) {
$professor = sanitize_text_field($data['professorId']);
$existQuery = new WP_Query(array(
'author' => get_current_user_id(),
'post_type' => 'like',
'meta_query' => array(
array(
'key' => 'liked_professor_id',
'compare' => '=',
'value' => $professor
)
)
));
if ($existQuery->found_posts == 0 AND get_post_type($professor) == 'professor') {
return wp_insert_post(array(
'post_type' => 'like',
'post_status' => 'publish',
'post_title' => '2nd PHP Test',
'meta_input' => array(
'liked_professor_id' => $professor
)
));
} else {
die("Invalid professor id");
}
} else {
die("Only logged in users can create a like.");
}
}
src/modules/Like.js:createLike() 请求补上 nonce 身份验证(直接照搬 My Notes 模块用过的写法):
// wp-content/themes/fictional-university-theme/src/modules/Like.js
createLike(currentLikeBox) {
$.ajax({
beforeSend: xhr => {
xhr.setRequestHeader("X-WP-Nonce", universityData.nonce)
},
url: universityData.root_url + "/wp-json/university/v1/manageLike",
type: "POST",
data: { "professorId": currentLikeBox.data("professor") },
success: response => {
console.log(response)
},
error: response => {
console.log(response)
}
})
}
single-professor.php:修复未登录用户被误判为「已点赞」的 bug(把 $existQuery 整段包进 is_user_logged_in() 判断里):
$existStatus = 'no';
if (is_user_logged_in()) {
$existQuery = new WP_Query(array(
'author' => get_current_user_id(),
'post_type' => 'like',
'meta_query' => array(
array(
'key' => 'liked_professor_id',
'compare' => '=',
'value' => get_the_ID()
)
)
));
if ($existQuery->found_posts) {
$existStatus = 'yes';
}
}
关键改动点:
is_user_logged_in()在 REST API 自定义端点的回调函数里,必须配合前端传来的 nonce(X-WP-Nonce请求头)才能正确判断为 true——即使浏览器里其实已经登录,只要请求没带 nonce,WordPress 出于安全考虑一律当作「未登录」处理,这也是这一讲一开始为什么明明登录了却依然被拒绝的原因- 「查重」和「新建」用的是同一套
meta_query逻辑(跟教授详情页判断「当前用户是否已点赞」的查询几乎一模一样),只是这里的value用前端传来的$professor变量而不是get_the_ID()——因为端点函数运行时不在任何「当前文章循环」的上下文里,没有get_the_ID()可用 if ($existQuery->found_posts == 0 AND get_post_type($professor) == 'professor'):两个条件都满足才真正创建——get_post_type($id)可以传入任意 ID 查询它的文章类型,如果这个 ID 不存在或者对应的不是professor类型,条件就不成立,用来防止有人传入博客文章、Campus 甚至完全瞎编的 ID- Bug 修复的核心原因:
get_current_user_id()在没有登录时返回整数0;但WP_Query参数里'author' => 0在 WordPress 的解析逻辑里等同于「没有传这个参数」,即「不限定作者,查全部」——所以未登录时这个查询依然会查到「别人」对这位教授的点赞记录,进而误判成「当前(其实并不存在的)用户已经点过赞」。解法是干脆不要在未登录时运行这个查询,直接把整段逻辑包进is_user_logged_in()判断里,未登录时$existStatus保持默认值'no'
[截图:未登录状态下访问教授详情页,心形图标正确显示为空心(未点赞),不再误判成已点赞]
Hook / Function 速查
| 名称 | 类型 | 用途 |
|---|---|---|
is_user_logged_in() | WP 内建 function | 判断当前请求是否来自已登录用户;在 REST 端点里必须配合正确的 nonce 才会返回 true |
get_post_type($post_id) | WP 内建 function | 查询指定 ID 的文章类型,可用来校验一个 ID 是否属于预期的类型 |
常见坑
WP_Query传'author' => get_current_user_id()时没考虑到未登录用户这个值是0——author => 0会被当成不限定作者,导致查询条件失效,产生「未登录却被判定为已点赞」这类反直觉的 bug- 只校验「是否已点过赞」却不校验「目标 ID 是不是真的是教授」——攻击者可以随便编一个不存在或指向别的文章类型的 ID 来绕过点赞记录的限制,两个校验条件缺一不可
- 忘记在前端请求里补上 nonce——即使服务端逻辑写得再对,没有 nonce 的话
is_user_logged_in()永远判定为 false,功能表现得就像完全没登录一样
延伸 / 后续讲座会用到
下一讲(part 3,本课的最后一部分)会让点赞成功后前端心形和数字实时更新(不用手动刷新),并且实现 deleteLike() 的真正业务逻辑。
Sources
Udemy:
- Become a WordPress Developer: Unlocking Power With Code — Section 20, EP107