WordPress内容锁定:短代码+密码+Cookie实现公众号导流
2026/9/15 17:35:48 网站建设 项目流程

简介:由博客 dsdiss.com 开发的一款名为 Dsdiss Verification 的 WordPress 插件,核心用途是隐藏文章部分内容,访客关注公众号并获取密码后方可查看,从而帮助内容型网站站长完成微信公众号引流。后台新增“二维码设置”页面,可配置验证密码、提示文字与二维码图片;通过 [protected] 短代码在文章中标注受保护片段,验证成功后会通过 cookie 记录状态,使用门槛低,非技术型运营者也能借助后台完成配置。压缩包为 zip 格式,整体仅 4KB,共 3 个文件:PHP 主文件负责后台设置与短代码解析,JS 脚本处理前端验证交互,CSS 文件控制隐藏区域样式。插件包虽小,但完整覆盖了从文章内容保护、用户输入密码、写入 cookie 到显示隐藏内容的闭环流程,也便于开发者理解 WordPress 插件开发中的设置 API、短代码注册、静态资源引入及验证状态管理等常见知识点。目前已有 48 人学习下载,适合微信公众号运营者、WordPress 站长以及希望学习内容隐藏类插件实现的初级开发者参考。

1. 不只是隐藏:微信公众号导流插件的验证机制与场景

如果一个 WordPress 站点只是把文章截断,用户根本不会去扫码关注公众号。真正让人愿意掏出手机扫码的,是那种“内容就在眼前但被锁住”的微妙心理。Dsdiss Verification 正是这类插件里非常轻量的一款,它的核心逻辑只有一个短代码[protected]:把文章某段内容包起来,访客从公众号那边拿到密码后输入验证,通过验证就靠 Cookie 记住状态,刷新后内容直接可见。它不引入会员体系,不校验微信 openid,也不改文章表结构,对现有站点几乎没有侵入性。适合放课程资料、下载链接、活动代码或者临时需要访问门槛的文章,也适合运营人员用来拉公众号的关注量。由于整个流程只有“密码 + Cookie + 短代码”三件事,排错和二次开发都容易。

2. 拆解 dsdiss-verification.php:设置项注册与短代码定义

把压缩包解压后,核心内容就三个文件:dsdiss-verification.php是主入口,style.css控制锁定框样式,script.js负责前端交互。按 WordPress 插件惯例,整个目录复制到/wp-content/plugins/dsdiss-verification/即可被识别。主文件开头通常有一份插件头注释,用来声明插件的名称和启用入口。

/* Plugin Name: Dsdiss Verification Description: 隐藏文章部分内容,验证密码后通过 Cookie 解锁 Version: 1.0 */

插件头注释是 WordPress 识别插件的基础,没有它后台插件列表会直接报“文件内容错误”。下面的资源加载和设置项注册都依赖这个主文件正常载入。理解了这个入口,后面看短代码和 AJAX 逻辑才不会乱。

2.1 插件文件结构与加载入口

文件作用
dsdiss-verification.php插件主入口,负责短代码注册、后台菜单、设置项和 AJAX 回调
style.css锁定提示框、二维码图片、表单的默认样式
script.js表单提交、AJAX 验证、写 Cookie、刷新页面

资源加载是第一个关键点。原文插件会在前台页面同时引入 CSS 和 JS,并提前准备好 AJAX 地址。常见做法是这样:

function dsdiss_enqueue_assets() { wp_enqueue_style('dsdiss-verification', plugin_dir_url(__FILE__) . 'style.css', array(), '1.0'); wp_enqueue_script('dsdiss-verification', plugin_dir_url(__FILE__) . 'script.js', array('jquery'), '1.0', true); wp_localize_script('dsdiss-verification', 'dsdiss_ajax', array( 'ajax_url' => admin_url('admin-ajax.php'), )); } add_action('wp_enqueue_scripts', 'dsdiss_enqueue_assets');

这里用plugin_dir_url(__FILE__)而不是硬编码路径,是因为插件目录可能被重命名,硬编码在迁移时第一个出问题。wp_localize_script把 AJAX 地址暴露给 JS 里的dsdiss_ajax.ajax_url,避免在脚本里写死域名。加载顺序上,JS 放到页脚位置,不阻塞文章页首屏渲染。

提示:如果前台样式没有生效,优先在浏览器开发者工具里看style.css是否返回 404,再检查插件目录名是否和代码拼出来的路径一致。

2.2 后台菜单与设置项注册

后台菜单的作用是让管理员能设置验证密码、提示内容和二维码图片。原插件在 WordPress 后台添加了一个“二维码设置”菜单,对应的结构可以还原为:

add_action('admin_menu', function () { add_menu_page( '二维码设置', '二维码设置', 'manage_options', 'dsdiss-verification', 'dsdiss_render_options_page' ); }); add_action('admin_init', function () { register_setting('dsdiss_settings_group', 'dsdiss_password'); register_setting('dsdiss_settings_group', 'dsdiss_tip_text'); register_setting('dsdiss_settings_group', 'dsdiss_qrcode_url'); });

add_menu_page的参数分别是页面标题、菜单标题、用户权限、菜单别名、回调函数。这里使用manage_options限制为管理员权限,避免订阅者进入后台设置页。register_setting的第一个参数是设置组名,必须和后面保存表单里settings_fields填写的组名完全一致,否则 WordPress 会拒绝保存并提示“操作不合法”。

从存储设计看,三个值各自存成独立 option,而不是合成一个数组,是为了读取时直接用get_option拿字符串,省去解析数组的步骤。缺点是设置项变多后会占更多wp_options表记录,单站点问题不大,多站点则需要考虑聚合存储。

2.3 短代码 [protected] 的解析与权限判断

短代码是整个插件的核心。文章编辑器里写了[protected]...[/protected],WordPress 解析时会调用注册的回调函数。原插件的判断逻辑很直白:读取全局密码,检查 Cookie 中的值与密码指纹是否一致,一致就输出隐藏内容,否则渲染锁定提示和表单。

add_shortcode('protected', 'dsdiss_protected_render'); function dsdiss_protected_render($atts, $content = null) { $password = get_option('dsdiss_password', ''); $verified = isset($_COOKIE['dsdiss_verified']) && $_COOKIE['dsdiss_verified'] === md5($password); if ($verified) { return do_shortcode($content); } $tip = get_option('dsdiss_tip_text', '请关注公众号后获取密码'); return sprintf( '<div class="dsdiss-locked">%s<form id="dsdiss-verify-form"><input type="password" name="dsdiss_password" /><button type="submit">验证</button></form></div>', esc_html($tip) ); }

这里用md5($password)做 Cookie 标记,只是防止明文密码直接出现在前端,严格来说它不是安全方案。因为 md5 值等于密码指纹,抓到 Cookie 后可以用彩虹表反查。生产环境我更倾向于用wp_hashhash_hmac('sha256', $password, wp_salt())生成标记。另外$content必须经过do_shortcode,否则隐藏内容里嵌套的其他短代码不会解析。

注意:$atts参数在当前版本没有实际参与判断,但它为后续扩展留下了空间,比如给短代码加id属性实现不同文章不同密码。这一点在后文会展开。

3. 前端验证链路:从密码框到 Cookie 标记

后台逻辑只负责判断“当前请求是否已通过验证”,真正的交互发生在script.js。用户点击验证按钮后,JS 把密码提交到 admin-ajax.php,PHP 返回成功标记,JS 把标记写入 Cookie,最后刷新页面让短代码重新渲染。整个过程不需要跳转,体验比传统表单提交更顺畅。

3.1 admin-ajax 接口与密码校验

WordPress 插件最常用的 AJAX 入口是admin-ajax.php。Dsdiss Verification 注册了wp_ajax_wp_ajax_nopriv_两个钩子,因为文章访客大多没有注册账号,必须允许匿名请求。

add_action('wp_ajax_dsdiss_verify', 'dsdiss_handle_verify'); add_action('wp_ajax_nopriv_dsdiss_verify', 'dsdiss_handle_verify'); function dsdiss_handle_verify() { $input = isset($_POST['password']) ? wp_unslash($_POST['password']) : ''; $pass = get_option('dsdiss_password', ''); if ($input === $pass) { wp_send_json_success(array( 'token' => md5($pass) )); } wp_send_json_error(array('message' => '密码错误,请重新核对公众号里的提示。'), 403); }

wp_send_json_success会自动输出 JSON 并结束请求,比手动拼接 JSON 再wp_die省事。wp_unslash是必须的,因为 WordPress 会默认给$_POST加 slashes,如果不处理,和 option 里保存的密码比对时大概率失败。从安全角度,这个接口缺少 nonce 校验,意味着理论上有爆破风险。自己部署时建议在表单里加wp_nonce_field,然后用check_ajax_referer校验。

3.2 script.js 的状态写入与刷新策略

script.js 的任务是拦截表单提交、发起 AJAX、写 Cookie、刷新页面。最直接的做法是:

jQuery(function($) { $(document).on('submit', '#dsdiss-verify-form', function(e) { e.preventDefault(); var pwd = $(this).find('input[name="dsdiss_password"]').val(); $.post(dsdiss_ajax.ajax_url, { action: 'dsdiss_verify', password: pwd }).done(function(res) { if (res.success) { document.cookie = 'dsdiss_verified=' + res.data.token + '; path=/; max-age=3600; SameSite=Lax'; window.location.reload(); } else { alert(res.data.message); } }).fail(function() { alert('验证请求失败,请检查网络后重试。'); }); }); });

max-age=3600表示 Cookie 有效期 1 小时,单位是秒;如果公众号固定每天更新口令,可以调整到 86400。path=/让 Cookie 在整个站点内生效,这样用户从文章甲跳到文章乙,只要两篇文章共用同一个密码,就不会重复验证。子目录安装站点要注意路径设置,否则会出现“这个页面验证成功,另一个页面又锁住”的问题。

这里不设置HttpOnly是因为 JS 需要显式写入 Cookie。代价是如果站点存在 XSS 漏洞,攻击者也能读到这个验证标记。更稳妥的方案是让 PHP 端校验后直接setcookie,再跳回当前页,但那样交互会多一次跳转。原插件选择的是 JS 写 Cookie,胜在简洁。

3.3 Cookie 参数与常见认知误区

Cookie 属性参数值影响
namedsdiss_verified对应 PHP 端读取$_COOKIE['dsdiss_verified']
valuemd5(password)避免明文密码进入前端,但不能防止重放
path/全站生效;子目录站点应使用 COOKIEPATH
max-age3600过期时间;太长导致验证状态长期有效
SameSiteLax阻止第三方站携带该 Cookie 发出跨站请求

一个常被忽略的点:刷新页面后,短代码回调里只比较$_COOKIE['dsdiss_verified'] === md5($password),并没有校验这个 Cookie 是否来自当前站点。浏览器本身会按域名隔离 Cookie,所以跨站点伪造风险不高,但同网段用户如果抓包拿到 Cookie 值,在自己的浏览器里伪造同样值也能生效。这就是密码不能设置太简单的原因,最好混合字母和数字。

4. 二维码设置与后台参数调优

设置页看起来只有三个字段,但每个字段都直接决定用户是否会扫码。很多部署后转化率低的案例,问题都出在提示文本太含糊、二维码加载失败或者表单和二维码不在同一屏。

4.1 三个核心字段的作用与取值建议

后台“二维码设置”页需要维护以下三个配置项:

设置项标识显示名称说明
dsdiss_password验证密码公众号自动回复或图文消息中给出的口令
dsdiss_tip_text提示内容锁定框中的引导文字,需要明确写出获取方式
dsdiss_qrcode_url二维码图片 URL公众号二维码图片的完整 URL,建议使用 HTTPS 绝对地址

密码建议用类似2025-x7k9这样的短口令,不要用超长随机字符串,用户从公众号复制到网页的过程很容易出错。提示文字要写清楚步骤,例如“打开微信扫一扫右侧二维码,关注后回复 dsdiss 获取访问密码”。二维码图片 URL 必须填完整地址,不能写相对路径,否则在部分浏览器和 App 内打开时图片会裂。

锁定框里需要通过短代码渲染二维码。常见做法是在返回表单前把二维码 URL 拼进提示区域:

$qr = get_option('dsdiss_qrcode_url', ''); if ($qr) { $tip .= sprintf( '<img src="%s" class="dsdiss-qrcode" alt="公众号二维码" />', esc_url($qr) ); }

这样二维码和表单就出现在同一个.dsdiss-locked容器里。样式方面,style.css里通常会把容器改成 flex 布局,让文字、二维码、验证按钮在桌面端并排,在移动端自动折行。二维码图片建议限制max-width: 180px,避免撑破文章内容宽度。

4.2 后台表单渲染与保存逻辑

设置表单的标准写法是:

function dsdiss_render_options_page() { ?> <div class="wrap"> <h1>二维码设置</h1> <form method="post" action="options.php"> <?php settings_fields('dsdiss_settings_group'); ?> <table class="form-table"> <tr><th>验证密码</th><td><input name="dsdiss_password" value="<?php echo esc_attr(get_option('dsdiss_password')); ?>" /></td></tr> <tr><th>提示内容</th><td><textarea name="dsdiss_tip_text"><?php echo esc_textarea(get_option('dsdiss_tip_text')); ?></textarea></td></tr> <tr><th>二维码图片 URL</th><td><input name="dsdiss_qrcode_url" value="<?php echo esc_attr(get_option('dsdiss_qrcode_url')); ?>" /></td></tr> </table> <?php submit_button(); ?> </form> </div> <?php }

表单的action指向options.php,由 WordPress 后台自动处理保存逻辑。settings_fields输出 nonce 和option_page隐藏字段,名称必须与register_setting的组名一致。填写字段时注意使用esc_attresc_textarea,防止后台设置页被恶意注入脚本。

如果后面想支持多密码,可以把dsdiss_password改成 JSON 字符串存储,但读取时要json_decode,并在短代码回调里根据$atts['id']选择对应密码。未指定id时最好回落到默认密码,保证旧文章不用改。

4.3 部署中的常见坑与排查方向

第一个最容易遇到的坑是缓存。站点启用页面缓存后,用户第一次看到锁定状态时页面被缓存,验证成功刷新后仍然返回缓存版本,Cookie 生效不了。处理办法是让缓存插件排除带dsdiss_verifiedCookie 的请求,或者干脆关闭含有[protected]页面的缓存。轻量站点可以直接禁用该文章页缓存。

第二个坑是二维码加载失败。如果后台填的是/wp-content/uploads/qr.png这样的相对路径,在前台网页上也许能访问,但在微信内置浏览器里可能因为基础路径变化被解析成错误地址。见到这种情况应改成https://your-domain.com/wp-content/uploads/qr.png的绝对地址,并避免使用短链接。

第三个坑是the_content过滤顺序。有的主题在输出文章内容时会对包含短代码的文本做wp_kses或其他转义,导致返回的 HTML 被吞掉。定位办法:先禁用所有其他插件,只保留本插件,如果锁定框正常显示,再逐个开启其他插件找到冲突源。

5. 扩展:让 [protected] 支持多篇文章不同密码

很多运营者做到第二个活动就会遇到问题:一篇活动文章一个密码,才能区分不同渠道的关注效果。原插件只支持一个全局密码,但短代码的属性参数本身就适合做这个扩展。比如把[protected id="course-a"]解析成读取dsdiss_pass_course-a,未指定id时回落到全局密码。

5.1 多密码短代码的实现

扩展后的短代码回调可以写成这样:

add_shortcode('protected', function ($atts, $content = null) { $atts = shortcode_atts(array('id' => 'default'), $atts, 'protected'); $password = 'default' === $atts['id'] ? get_option('dsdiss_password', '') : get_option('dsdiss_pass_' . $atts['id'], ''); $verified = isset($_COOKIE['dsdiss_verified_' . $atts['id']]) && $_COOKIE['dsdiss_verified_' . $atts['id']] === md5($password); if ($verified) { return do_shortcode($content); } $tip = get_option('dsdiss_tip_text', '请关注公众号获取密码'); return '<div class="dsdiss-locked">' . esc_html($tip) . '</div>'; });

这里的关键是 Cookie 名也带上了id,避免多篇文章密码互相覆盖。如果忽略这一点,用户验证完 A 文章后,B 文章也会被误判为已解锁。后台的接口回调同样要接收一个shortcode_id字段,返回对应密码的 token,否则前端的 Cookie 永远写不进去。

5.2 验证方法与样式细节

部署后可以用浏览器开发者工具验证:打开“应用”面板,查看dsdiss_verified_course-a的 value 是否等于对应密码的 md5 值。确认一致后刷新页面,如果该篇文章的隐藏内容可见,而另一篇使用不同id的文章仍然锁定,说明隔离逻辑正确。

最后一个细节:锁定框里的二维码图片建议在style.css里设置max-width: 180px; border-radius: 8px;,并让图片与提示文字垂直居中。小屏手机用户扫码时,尺寸过大的二维码会被截断,影响整体转化率。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询