本页目录
自发布插件更新检查器库以来,提出的最常见问题之一是:“如何保护我的下载链接,以便只有购买了我的插件的用户才能下载更新?”
在本文中,我将尝试在使用 WP Update Server 提供插件更新的上下文中回答该问题。我还将从我的一个商业插件中提供一些实际示例。
让我们开始吧。有许多方法可以确保更新下载的安全,但是大多数方法可以归结为:
- 给每个用户某种安全令牌。这可以是会员站点的登录名+密码,许可证密钥,或者更深奥的东西。
- 每当有人在其站点上安装您的插件时,都要求他们输入密钥/登录/以其他方式输入。
- 修改插件以将令牌附加到每个更新请求。
- 编写一个服务器脚本,该脚本在允许下载之前验证令牌。
选择一个安全令牌
实施前两个步骤的方式会因插件 UI 以及所使用的在线商店,购物车软件或成员资格插件而有很大差异。您可能已经存在某种客户身份验证机制,只需要进行一些调整即可用于更新,或者您可能需要从头开始构建自己的身份验证机制。这里没有“一刀切”的解决方案。
就个人而言,我更喜欢使用许可证密钥。每当有人购买我的 Admin Menu Editor Pro 插件时,订单处理脚本都会生成一个随机密钥,将其存储在数据库中,然后将密钥和下载链接发送到客户的电子邮件中。然后,当他们安装插件时,将提供一个输入许可证密钥的链接。
我不会在此处包括许可证管理代码,因为它不在本文的讨论范围内,而是为该特定插件构建的,但是用户界面看起来像这样(单击放大):


将令牌添加到更新请求
现在,我们如何将安全令牌添加到每个更新请求中?您可以使用 addQueryArgFilter($callback)更新检查器的方法来执行此操作。回调函数将接收查询参数的关联数组。只需将令牌添加到列表中并返回修改后的数组即可。
这是一个例子:
/* ... Code that initializes the update checker ... */
//Add the license key to query arguments.$updateChecker->addQueryArgFilter('wsh_filter_update_checks');function wsh_filter_update_checks($queryArgs) { $settings = get_option('my_plugin_settings'); if ( !empty($settings['license_key']) ) { $queryArgs['license_key'] = $settings['license_key']; } return $queryArgs;}使用令牌授权下载
最后,让更新服务器在允许用户下载更新之前验证安全令牌。为此,您需要创建一个自定义服务器类(请参阅*扩展服务器)并至少*重写该
Wpup_UpdateServer::checkAuthorization($request)方法。这是使用此方法应做的事情:
- 通过使用检索包含令牌的查询参数$request->param(‘arg_name’)。
- 验证令牌。同样,这部分取决于您。您可以在数据库中查找它,使用校验和来验证它,或进行其他操作。
- 如果令牌是好的,则无需执行任何特殊操作。
- 如果令牌无效,请调用$this->exitWithError(‘Error message’)以输出错误并停止脚本执行。
以下是脚本的简化版本,用于为 Admin Menu Editor Pro 实施安全更新 。它比上面的概述要先进一些,但是总体思路是相同的。
(同样,许可证管理超出了本文的范围,因此,我省略了大多数与加载和验证许可证有关的代码。只需将 verifyLicenseExists()和其他许可功能视为伪代码。)
class SecureUpdateServer extends Wpup_UpdateServer { protected $licenseServer;
public function __construct($serverUrl, $licenseServer) { parent::__construct($serverUrl); $this->licenseServer = $licenseServer; }
protected function initRequest($query = null, $headers = null) { $request = parent::initRequest($query, $headers);
//Load the license, if any. $license = null; if ( $request->param('license_key') ) { $result = $this->licenseServer->verifyLicenseExists( $request->slug, $request->param('license_key') ); if ( is_wp_error($result) ) { //If the license doesn't exist, we'll output an invalid dummy license. $license = new Wslm_ProductLicense(array( 'status' => $result->get_error_code(), 'error' => array( 'code' => $result->get_error_code(), 'message' => $result->get_error_message(), ), )); } else { $license = $result; } }
$request->license = $license; return $request; }
protected function filterMetadata($meta, $request) { $meta = parent::filterMetadata($meta, $request);
//Include license information in the update metadata. This saves an HTTP request //or two since the plugin doesn't need to explicitly fetch license details. $license = $request->license; if ( $license !== null ) { $meta['license'] = $this->licenseServer->prepareLicenseForOutput($license); }
//Only include the download URL if the license is valid. if ( $license && $license->isValid() ) { //Append the license key or to the download URL. $args = array( 'license_key' => $request->param('license_key') ); $meta['download_url'] = self::addQueryArg($args, $meta['download_url']); } else { //No license = no download link. unset($meta['download_url']); }
return $meta; }
protected function checkAuthorization($request) { parent::checkAuthorization($request);
//Prevent download if the user doesn't have a valid license. $license = $request->license; if ( $request->action === 'download' && ! ($license && $license->isValid()) ) { if ( !isset($license) ) { $message = 'You must provide a license key to download this plugin.'; } else { $error = $license->get('error'); $message = isset($error) ? $error : 'Sorry, your license is not valid.'; } $this->exitWithError($message, 403); } }}来源于: https://w-shadow.com/blog/2013/03/19/plugin-updates-securing-download-links/