本文分享如何为你的 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="your@email.com" />
<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 签名验证请求来源
-
时间戳 防止重放攻击
-
用户标识 防止重复领取
希望这篇教程对你有所帮助,欢迎评论区讨论!