ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

用Cloudflare Worker实现验证码邮件发送

用Cloudflare Worker实现验证码邮件发送 上一期我们聊了热土引擎邮件代发的优缺点——省去自建 SMTP 转发的麻烦但模板一旦创建就不能在运行时修改内容只能靠占位符做变量替换。今天我们从 Cloudflare Worker 开发者的视角把“发送一封验证码邮件”这件事从头到尾跑通。第一步在开发者面板把“料”备好在写任何代码之前你需要先在 dev.rtstu.com 完成三件事生成 mail_key。进入「邮件 → 邮件代发」页面点击生成。这个 32 位的字符串就是你的身份凭证明文只显示一次务必立刻保存。配置 SMTP 和邮箱模板。邮箱模板定义邮件的“骨架”。比如一个验证码模板可以写成h3$parm1$ 的验证/h3p您的验证码是$parm2$/p/p本邮件由 $parm3$ 发送/p$parm1$、$parm2$、$parm3$就是运行时会被替换的占位符。创建发送模板。发送模板和邮箱模板是“多对多”的关系发送时指定send_template_id即可。文档里的示例中mail_template_id 1send_template_id 1两者归属同一开发者即可通过校验。第二步Worker 代码逐段拆解下面是一个完整的 Worker 实现。我们逐段来看。环境变量与入口exportdefault{asyncfetch(request,env){// CORS 预检if(request.methodOPTIONS){returnnewResponse(null,{headers:{Access-Control-Allow-Origin:*,Access-Control-Allow-Methods:POST, OPTIONS,Access-Control-Allow-Headers:Content-Type,},});}Worker 作为前端和后端之间的中间层需要处理 CORS 预检请求。env对象里存放我们后面要用到的密钥这些通过wrangler secret put或 Dashboard 环境变量注入不写死在代码里。if(request.method!POST){returnnewResponse(Method Not Allowed,{status:405});}const{email}awaitrequest.json();if(!email){returnnewResponse(JSON.stringify({error:缺少邮箱地址}),{status:400,headers:{Content-Type:application/json},});}只接受 POST从请求体中取出收件人邮箱。这一步是验证码服务的安全入口——不能让任何人随便调用你的发信额度。生成验证码并存入 KVconstcodeString(Math.floor(100000Math.random()*900000));constexpireAtDate.now()5*60*1000;// 5 分钟有效awaitenv.VERIFY_KV.put(verify:${email},JSON.stringify({code,expireAt,attempts:0,}),{expirationTtl:300});用 KV 存验证码是最轻量的方案。expirationTtl: 300让 Cloudflare 自动清理过期记录不用自己写定时任务。attempts字段后面可以用来限制验证尝试次数防止暴力破解。调用热土引擎发送邮件这是核心部分。constmailPayload{mail_key:env.RTSTU_MAIL_KEY,mail_template_id:env.RTSTU_MAIL_TEMPLATE_ID,send_template_id:env.RTSTU_SEND_TEMPLATE_ID,to:email,parm1:用户,parm2:code,parm3:你的应用名,};parm1到parm3对应邮箱模板里的三个占位符。文档中有一条重要规则占位符仅在模板中真正出现时才会被替换传入未使用的占位符会被静默忽略。所以你可以放心地在模板里只放两个甚至一个占位符多余的参数不会导致错误。mail_key、mail_template_id、send_template_id三者必须归属于同一个开发者 ID否则接口会拒绝发送。constrespawaitfetch(https://appapi.rtstudio.top/mail/send,{method:POST,headers:{Content-Type:application/json},body:JSON.stringify(mailPayload),});constresultawaitresp.json();if(!result.success){// 邮件发送失败清理刚写入的 KV 记录awaitenv.VERIFY_KV.delete(verify:${email});returnnewResponse(JSON.stringify({error:result.message}),{status:502,headers:{Content-Type:application/json},});}热土引擎的响应格式是统一的成功时success: true失败时success: false并附带message字段说明原因比如“邮件发送 Key 无效”。这里做了一个重要处理邮件发送失败时删除 KV 中的验证码。否则用户收到一个永远收不到的验证码记录下次请求时可能被旧记录干扰。returnnewResponse(JSON.stringify({success:true}),{headers:{Content-Type:application/json,Access-Control-Allow-Origin:*,},});},};成功时只返回{ success: true }不回传验证码本身——这是基本的安全底线。几个容易踩的坑速率限制是真实存在的。热土引擎的邮件接口和所有 appapi 接口一样受 Cloudflare WAF 的 IP 级速率限制约束同一 IP 10 秒内最多 30 次请求第 31 次直接返回 429 并阻断 10 秒。在 Worker 里这意味着如果你的多个用户共享同一个出口 IP或者你的 Worker 被脚本高频调用都可能触发限速。文档里建议遇到 429 时使用指数退避不要在循环里立即重试。mail_key 不要暴露在前端。Worker 的价值就在于它是一层服务端代理RTSTU_MAIL_KEY作为 Worker 的环境变量存储前端只和 Worker 通信永远不接触真实的 mail_key。模板 ID 是整数。mail_template_id和send_template_id在开发者面板里显示为数字但在环境变量中注入时会被存为字符串。记得在 Worker 里显式转换Number(env.RTSTU_MAIL_TEMPLATE_ID)否则某些运行时可能不会自动做类型转换。验证码的验证逻辑不在这里。上面只处理了“发送”部分。实际的“校验”应该是一个独立的 Worker 路由或函数从 KV 中取出verify:${email}检查expireAt是否过期、attempts是否超限比对成功后删除记录。这部分逻辑和热土引擎无关完全由你自己的 Worker 控制。最后热土引擎的邮件代发本质上是一个“模板化的邮件发送网关”。它把 SMTP 连接、发信域名声誉、DNS 配置这些头疼的事打包好了你只需要关心三件事模板 ID、收件人、占位符的值。对于在 Cloudflare Worker 上做验证码发送这个场景来说这个抽象层次是合适的——不多不少。下一期可以聊聊验证码的校验逻辑怎么写才安全以及如何用 Worker 的scheduled事件做发送频率的二次限制。
返回列表