月明星稀

制作一个"登录或者评论领取激活码"插件

本文分享如何为你的网站实现一个自动化激活码领取系统,支持 WordPress、Typecho 和静态博客(Astro/Hugo)。

本文分享如何为你的 App 实现一个自动化激活码领取系统,支持 WordPress、Typecho 和静态博客(Astro/Hugo)。

一、后端 API(Flask)

后端负责:生成激活码、验证请求签名、防止重复领取。

1.1 激活码模型


from flask_sqlalchemy import SQLAlchemy

import random

  

db = SQLAlchemy()

  

class ActivationCode(db.Model):

id = db.Column(db.Integer, primary_key=True)

code = db.Column(db.String(20), unique=True, nullable=False)

device_id = db.Column(db.String(64)) # 绑定的设备

note = db.Column(db.String(200)) # 领取信息

is_active = db.Column(db.Boolean, default=True)

created_at = db.Column(db.DateTime, default=datetime.utcnow)

  

def generate_activation_code():

"""生成格式化激活码:XXXX-XXXX-XXXX-XXXX"""

chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ0123456789'

segments = [''.join(random.choices(chars, k=4)) for _ in range(4)]

return '-'.join(segments)

1.2 领取接口


@app.route('/api/get-code', methods=['POST'])

def claim_code():

data = request.get_json()

query = data.get('query', '').strip() # 用户唯一标识

secret = data.get('secret', '') # 密钥

timestamp = data.get('timestamp') # 时间戳

signature = data.get('signature', '') # HMAC 签名

# 1. 验证密钥

if secret != app.config['CLAIM_SECRET']:

return jsonify({'success': False, 'message': '密钥错误'}), 401

# 2. 验证时间戳(5分钟内有效,防止重放攻击)

if abs(time.time() - int(timestamp)) > 300:

return jsonify({'success': False, 'message': '请求已过期'}), 400

# 3. 验证签名

expected_sig = hmac.new(

secret.encode(),

f"{query}|{timestamp}|{secret}".encode(),

hashlib.sha256

).hexdigest()

if not hmac.compare_digest(signature, expected_sig):

return jsonify({'success': False, 'message': '签名验证失败'}), 401

# 4. 检查是否已领取

existing = ActivationCode.query.filter(

ActivationCode.note.like(f"Claimed by: {query} |%")

).first()

if existing:

return jsonify({'success': True, 'code': existing.code})

# 5. 生成新激活码

code = generate_activation_code()

new_code = ActivationCode(

code=code,

note=f"Claimed by: {query} | {data.get('username')} | {data.get('email')}"

)

db.session.add(new_code)

db.session.commit()

return jsonify({'success': True, 'code': code})

1.3 安全措施

威胁防御措施
接口被滥用HMAC-SHA256 签名验证
重放攻击时间戳 5 分钟有效期
重复领取数据库 note 字段记录用户标识

二、WordPress 插件

WordPress 可以自动获取登录用户信息,实现”一键领取”。

2.1 插件结构

文件:wp-content/plugins/activation-claimer/activation-claimer.php


<?php

/*

Plugin Name: Activation Code Claimer

Description: 允许已登录用户一键领取激活码

Version: 2.0

*/

  

if (!defined('ABSPATH')) exit;

  

class ActivationCodeClaimer {

private $api_url = 'https://your-api.com/api/get-code';

private $api_secret = 'your-secret-key';

private $claimed_meta_key = 'activation_code_claimed';

  

public function __construct() {

add_shortcode('claim_activation', [$this, 'render_form']);

}

  

public function render_form($atts) {

// 检查登录状态

if (!is_user_logged_in()) {

$login_url = wp_login_url(get_permalink());

return '<div class="acc-error">请先<a href="'.$login_url.'">登录</a></div>';

}

  

$user = wp_get_current_user();

$user_id = $user->ID;

  

// 检查是否已领取

$claimed_code = get_user_meta($user_id, $this->claimed_meta_key, true);

if ($claimed_code) {

return '<div class="acc-success">您的激活码:<code>'.$claimed_code.'</code></div>';

}

  

// 处理表单提交

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['claim_submit'])) {

return $this->handle_claim($user);

}

  

// 渲染表单

return $this->render_claim_form($user);

}

  

private function handle_claim($user) {

// 验证 Nonce

if (!wp_verify_nonce($_POST['acc_nonce'], 'acc_claim_action')) {

return '<div class="acc-error">安全验证失败</div>';

}

  

// 生成签名

$timestamp = time();

$user_identifier = 'wp_user_' . $user->ID;

$signature = hash_hmac('sha256',

"$user_identifier|$timestamp|{$this->api_secret}",

$this->api_secret

);

  

// 调用后端 API

$response = wp_remote_post($this->api_url, [

'body' => json_encode([

'query' => $user_identifier,

'username' => $user->user_login,

'email' => $user->user_email,

'timestamp' => $timestamp,

'signature' => $signature,

'secret' => $this->api_secret

]),

'headers' => ['Content-Type' => 'application/json'],

'timeout' => 15

]);

  

$data = json_decode(wp_remote_retrieve_body($response), true);

if ($data['success']) {

update_user_meta($user->ID, $this->claimed_meta_key, $data['code']);

return '<div class="acc-success">领取成功:<code>'.$data['code'].'</code></div>';

}

return '<div class="acc-error">'.$data['message'].'</div>';

}

}

  

new ActivationCodeClaimer();

2.2 使用方法

在文章或页面中插入短代码:


[claim_activation]

三、Typecho 插件

3.1 插件结构

文件夹:usr/plugins/ClaimActivation/Plugin.php


<?php

class ClaimActivation_Plugin implements Typecho_Plugin_Interface {

public static function activate() {

// 注册内容过滤器

Typecho_Plugin::factory('Widget_Abstract_Contents')

->contentEx = ['ClaimActivation_Plugin', 'contentFilter'];

return _t('插件已激活,使用 <!--claim--> 标记插入领取表单');

}

  

public static function deactivate() {}

  

public static function config(Typecho_Widget_Helper_Form $form) {

$form->addInput(new Typecho_Widget_Helper_Form_Element_Text(

'apiUrl', NULL, 'https://your-api.com/api/get-code',

_t('后端 API 地址')

));

$form->addInput(new Typecho_Widget_Helper_Form_Element_Text(

'apiSecret', NULL, 'your-secret-key',

_t('API 密钥')

));

}

  

public static function personalConfig(Typecho_Widget_Helper_Form $form) {}

  

// 内容过滤器:替换 <!--claim--> 标记

public static function contentFilter($content, $widget, $lastResult) {

$content = empty($lastResult) ? $content : $lastResult;

if (strpos($content, '<!--claim-->') !== false) {

$form = self::generateClaimForm();

$content = str_replace('<!--claim-->', $form, $content);

}

return $content;

}

  

private static function generateClaimForm() {

$user = Typecho_Widget::widget('Widget_User');

$options = Typecho_Widget::widget('Widget_Options');

$pluginOptions = $options->plugin('ClaimActivation');

  

// 未登录

if (!$user->hasLogin()) {

return '<div class="acc-error">请先<a href="'.$options->adminUrl.'">登录</a></div>';

}

  

// ... 其余逻辑与 WordPress 类似

// 检查是否已领取 → 生成签名 → 调用 API → 显示结果

}

  

private static function claimCode($userId, $username, $email, $apiUrl, $apiSecret) {

$timestamp = time();

$userIdentifier = 'typecho_user_' . $userId;

$signature = hash_hmac('sha256',

"$userIdentifier|$timestamp|$apiSecret",

$apiSecret

);

  

// 使用 cURL 发送请求

$ch = curl_init($apiUrl);

curl_setopt_array($ch, [

CURLOPT_POST => true,

CURLOPT_POSTFIELDS => json_encode([

'query' => $userIdentifier,

'username' => $username,

'email' => $email,

'timestamp' => $timestamp,

'signature' => $signature,

'secret' => $apiSecret

]),

CURLOPT_HTTPHEADER => ['Content-Type: application/json'],

CURLOPT_RETURNTRANSFER => true,

CURLOPT_TIMEOUT => 15

]);

$response = curl_exec($ch);

curl_close($ch);

return json_decode($response, true);

}

  

// 领取记录存储在 Typecho 的 options 表

private static function getClaimedCodes() {

$db = Typecho_Db::get();

$row = $db->fetchRow($db->select('value')

->from($db->getPrefix() . 'options')

->where('name = ?', 'plugin_ClaimActivation_claimed'));

return $row ? json_decode($row['value'], true) : [];

}

}

3.2 使用方法

在文章中插入 HTML 注释标记:


<!--claim-->

四、静态博客(Astro)

静态博客无服务端,需要纯前端 JavaScript + 后端 API。

4.1 Astro 组件

文件:src/components/ClaimCode.astro


---

interface Props {

apiUrl?: string;

}

const { apiUrl = 'https://your-api.com/api/claim-with-twikoo' } = Astro.props;

---

  

<div class="claim-wrapper" data-api-url={apiUrl}>

<div class="form-header">

<span class="icon">🎁</span>

<h3>领取激活码</h3>

<p>请输入您评论时使用的邮箱</p>

</div>

<div class="form-body">

<input type="email" id="claim-email" placeholder="[email protected]" />

<button id="claim-btn">领取激活码</button>

<p class="hint">💬 请先在下方评论区留言,使用相同邮箱即可领取</p>

</div>

<div id="result" style="display: none;"></div>

</div>

  

<style>

.claim-wrapper {

max-width: 420px;

padding: 1.5rem;

border-radius: 16px;

background: linear-gradient(135deg, #667eea10, #764ba210);

border: 1px solid #e0e0e0;

}

/* ... 更多样式 */

</style>

  

<script>

const wrapper = document.querySelector('.claim-wrapper');

const emailInput = document.getElementById('claim-email');

const claimBtn = document.getElementById('claim-btn');

const resultEl = document.getElementById('result');

const apiUrl = wrapper?.dataset.apiUrl;

  

claimBtn?.addEventListener('click', async () => {

const email = emailInput.value.trim();

if (!email || !email.includes('@')) {

showResult('error', '请输入有效的邮箱地址');

return;

}

  

claimBtn.disabled = true;

claimBtn.textContent = '验证中...';

  

try {

const res = await fetch(apiUrl, {

method: 'POST',

headers: { 'Content-Type': 'application/json' },

body: JSON.stringify({ email })

});

const data = await res.json();

  

if (data.success) {

showResult('success', `🎉 领取成功!<br><code>${data.code}</code>`);

emailInput.disabled = true;

claimBtn.style.display = 'none';

} else {

showResult('error', data.message);

claimBtn.disabled = false;

claimBtn.textContent = '领取激活码';

}

} catch (e) {

showResult('error', '网络错误,请稍后重试');

}

});

  

function showResult(type, message) {

resultEl.className = type;

resultEl.innerHTML = message;

resultEl.style.display = 'block';

}

</script>

4.2 评论验证(可选)

如果使用 Twikoo 评论系统,可以在后端验证用户是否评论过:


def verify_twikoo_comment(email):

from pymongo import MongoClient

client = MongoClient('mongodb+srv://...')

db = client['twikoo']

comment = db.comment.find_one({

'mail': {'$regex': f'^{email}$', '$options': 'i'}

})

client.close()

return comment is not None

4.3 在页面中使用


---

import ClaimCode from '../components/ClaimCode.astro';

---

  

<ClaimCode apiUrl="https://your-api.com/api/claim-with-twikoo" />

  

<!-- Twikoo 评论区 -->

<div id="tcomment"></div>

五、UI 样式参考

三种插件都使用了统一的样式设计:


.acc-wrapper {

max-width: 420px;

padding: 25px;

border: 1px solid #e0e0e0;

border-radius: 12px;

background: #fafafa;

}

  

.acc-btn {

background: linear-gradient(135deg, #667eea, #764ba2);

color: white;

border: none;

padding: 14px 28px;

border-radius: 8px;

width: 100%;

font-size: 16px;

cursor: pointer;

}

  

.acc-btn:hover {

transform: translateY(-2px);

box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);

}

  

.acc-success {

background: #d4edda;

border: 1px solid #c3e6cb;

color: #155724;

padding: 20px;

border-radius: 8px;

text-align: center;

}

  

.acc-code-display {

font-family: 'Courier New', monospace;

font-size: 1.4em;

font-weight: bold;

background: #fff;

padding: 10px 15px;

border: 2px dashed #28a745;

border-radius: 6px;

letter-spacing: 2px;

}

六、部署清单

步骤说明
1. 部署后端Flask API 部署到服务器
2. 配置 CORS允许前端域名跨域访问
3. 设置密钥前后端使用相同的 CLAIM_SECRET
4. 安装插件根据博客类型选择对应插件
5. 测试验证使用不同用户测试领取流程

总结

博客类型用户身份来源使用方式
WordPress登录用户[claim_activation] 短代码
Typecho登录用户<!--claim--> 标记
Astro/静态用户输入邮箱<ClaimCode /> 组件

核心安全机制:

  • HMAC-SHA256 签名验证请求来源

  • 时间戳 防止重放攻击

  • 用户标识 防止重复领取

希望这篇教程对你有所帮助,欢迎评论区讨论!