自定义邮箱验证码服务的实现

Cloudflare Workers 最佳实践

Posted by Momoka7 on September 18, 2024

为了防止滥用注册,实现一人一户,很多网站都需要通过邮箱/手机验证码来验证后才注册。那么这篇文章将介绍一下无成本的验证码邮箱验证码服务,并可以无缝接入到自己的已有系统中。

1. 如何拥有自己的域名邮箱

前提:拥有一个域名

1.1 在 Cloudflare 中添加电子邮件路由

  1. 点击自己的域名,进入电子邮件->电子邮件路由
  2. (若是第一次进入,跳过指引)点击路由规则面板。
  3. 点击启用电子路由,随后将其提供的四个 DNS 记录添加记录并启用
  4. Catch-all地址编辑
  5. 选择发送到电子邮件目标位置输入自己要接受/发送的邮箱地址。
  6. 随后 Cloudflare 会发送一封验证邮箱到你填写的邮箱中,进行验证即可。

这样的就可以实现“无限”域名下邮箱邮件了,这是因为任何发送到以你域名结尾的游戏都会转发到你填写的目标邮箱中了。

1.2 使用 Resend 实现邮件的发送

  1. 首先注册一个账号
  2. API Keys面板上Create API Key,并保存其生成的 API Key。
  3. Domain面板上Add domain, 填入自己的域名即可。
  4. 其会提供 3 个 DNS 记录,需要将其添加到 Cloudflare 的 DNS 记录,这里不赘述了。
  5. 随后在Domain面板,点击Verify DNS Record,看到三个 DNS 记录的状态都变为Verify即可。
  6. 测试邮件发送:点击Emails面板,右上角会有一个API按钮,可以通过其来发送邮件,如下。这里的from需要为以自己域名结尾的邮箱地址

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    import { Resend } from "resend";
    
    const resend = new Resend("re_123456789");
    
    await resend.emails.send({
      from: "Acme <onboarding@resend.dev>",
      to: ["delivered@resend.dev"],
      subject: "hello world",
      html: "<p>it works!</p>",
    });
    

这样就能实现通过代码来发送邮箱了。

2. 邮箱验证码 Worker

创建 Cloudflare 的 Worker 步骤就不赘述了,可以查看之前的文章。

示例本身很简单,实现过程中的细节后续会再提一提。

首先安装两个依赖:

1
2
npm install hono
npm install resend

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import { Hono } from "hono";
import { Resend } from "resend";

const app = new Hono();

// 发送验证码接口
app.post("/send-code", async (c) => {
  const resend = new Resend(c.env.MAIL_API_KEY);
  const KV = c.env.EMAILCODE;

  // 从请求体中获取用户邮箱
  const { email } = await c.req.json();

  // 生成6位数验证码
  const code = Math.floor(100000 + Math.random() * 900000).toString();

  // 将验证码存储到 KV,并设置过期时间,比如5分钟(300秒)
  await KV.put(email, code, { expirationTtl: 300 });

  // 发送验证码到目标邮箱
  await resend.emails.send({
    from: c.env.SENDER_EMAIL,
    to: [email],
    subject: "Your Verification Code",
    html: `<p>Your verification code is <strong>${code}</strong>. It will expire in 5 minutes.</p>`,
  });

  return c.json({ message: "Verification code sent successfully." });
});

// 验证验证码接口
app.post("/verify-code", async (c) => {
  const KV = c.env.EMAILCODE;

  // 从请求体中获取邮箱和验证码
  const { email, code } = await c.req.json();

  // 从 KV 中获取存储的验证码
  const storedCode = await KV.get(email, "text");

  // 比较验证码是否匹配
  if (storedCode === code) {
    // 验证码匹配,删除 KV 中的验证码
    await KV.delete(email);
    return c.json({ success: true, message: "Verification successful!" });
  } else {
    return c.json({ success: false, message: "Invalid or expired code." });
  }
});

// 默认首页
app.get("/", (c) => {
  return c.json({ message: "Hello, Hono!" });
});

// 修改 export 为 app
export default app;

环境变量和敏感信息的管理细节

环境变量顾名思义就是会随着使用者变化的一些配置信息,其中分为非敏感和敏感信息两类。

非敏感信息一般是一些只有使用者才能使用的信息,或者说其他人知晓了也无关痛痒的信息。

敏感信息一般是利益相关,其他人的使用会影响信息拥有者的信息,比如各种API_KEY,这种信息不能公开,尤其是要开源的情况下。

(1) 环境变量的使用

在 Cloudflare wrangler 的工程中,有多种方式可以设置环境变量:

wrangler.toml中设置,比如设置发送邮箱:

1
2
[vars]
SENDER_EMAIL = "noreply@xxx.xxx"

创建.dev.vars文件,可以在此设置开发环境的变量,比如 API_KEY,但是只能在开发环境中使用:

1
MAIL_API_KEY='your_api_key'

wrangler.toml中可以设置一些非敏感数据常量,.dev.vars可以设置敏感数据信息,因为其只在开发环境中使用,且默认添加到了.gitignore文件中,不用担心会上传到代码托管仓库中。

那么如何在生产环境中的 worker 中使用敏感信息?这时就需要使用到Secrets了。

(2)Secrets 的使用

首先说明,Secrets 只有在生产环境才生效,在本地的开发环境不生效。总之需要 deploy 后才能访问,使用方式和普通的环境变量一样。

需要通过npx wrangler secret put <key>来添加 Secrets,执行上述指令后会要求以密文的方式输入你的 value。

通过npx wrangler secret list查看当前 worker 中的 Secrets 列表,不过不会输出具体值。也可以在 Cloudflare 的控制台中查看:Workers和Pages-> 你的具体workers-> 设置 -> 变量