go实战之注册邮箱验证(末尾有彩蛋)

鉴权验证是一个网站必不可缺的东西,那么应该如何实现邮箱验证功能呢?下面将介绍实现的全过程以及使用到的库

技术栈

  • gin
  • redis
  • gomail
  • template

定义发送路由

因为后续可能会扩展鉴权认证功能,所以给定义一个路由组auth,请求meth为POST,请求路径为/auth/email/verifyCode,该路由接受一个mail字段来获取前端传入的邮箱

authentication := e.Group("/auth")
{
    mail := authentication.Group("/email")
    {
        mail.POST("/verifyCode", email.VerifyCode)
    }
}

核心实现

初始化 gomail

func NewMail() *gomail.Dialer {
    d := gomail.NewDialer("smtp.163.com",
        465,
        "xxxxxxxx@163.com",
        "LZYZVPAHNRCK",  // 授权码
    )
    return d
}

GenerateVerifyCode 生成验证码,使用时间戳作为随机种子,生成大写字母与数字,长度为4的随机验证码,并通过封装Redis.Set()设定验证码过期时间。

func (c *BaseContext) GenerateVerifyCode(email string) {

    rand.Seed(time.Now().UnixNano())

    const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    var result []byte
    // 四位大写英文字母与数字混合验证码
    for i := 0; i < 4; i++ {
        result = append(result, charset[rand.Intn(len(charset))])
    }

    // 验证码 5 分钟内有效
    c.Set(email, result, time.Minute*5)
}

发送验证码

// SendMail 发送验证码到邮箱
func (c *BaseContext) SendMail(t string, code string) {
    // 解析邮件模板
    tmpl, err := template.ParseFiles("web/templates/mail.html")
    if err != nil {
        log.Println("mail.html not found in path")
        return
    }
    // 构造邮件标题和验证码
    data := gin.H{
        "title": t,
        "code":  code,
    }
    // execTmpl 实现 io.Writer
    execTmpl := bytes.Buffer{}

    // 处理模板数据,写入到 execTmpl 中
    err = tmpl.Execute(&execTmpl, data)
    if err != nil {
        return
    }

    // 转化成字符串
    mailTmpl := execTmpl.String()

    // 发送
    c.SendMailStructure(t, mailTmpl, "xxxxxxxx@163.com")
}

设置邮件结构(或者说组织形式)

func (c *BaseContext) SendMailStructure(title string, code string, to string) *BaseContext {
    m := gomail.NewMessage()
    // 发件箱
    m.SetHeader("From", "cccccccc@163.com")
    // 收件箱名称
    m.SetHeader("To", to)
    //// 抄送
    //m.SetAddressHeader("Cc", "xxxxxxxx@163.com", "Dan")
    // 标题
    m.SetHeader("Subject", title)
    // 内容
    m.SetBody("text/html", code)

    // Send the email to Bob, Cora and Dan.
    if err := c.D.DialAndSend(m); err != nil {
        return nil
    }
    return c
}

gin HandleFunc 处理发送验证

func VerifyCode(c *gin.Context) {
    // 实例上下文
    btx := service.NewContext(c)

    // 获取需验证邮箱
    mail := btx.Ctx.PostForm("mail")

    // 生成验证码写入到 redis
    btx.GenerateVerifyCode(mail)

    // 获取验证码
    code, err := btx.Get(mail)
    if err != nil {
        log.Println("get verify code failed: ", err)
        return
    }
    btx.SendMail("this a verify code title", code)
    return
}

得益与GPT生成的邮件模板,使得能够快速开发出邮件验证系统,嵌入 {{.code}}和{{.title}}到模板中渲染后即可发送

<!DOCTYPE html>
<html>
<head>
    <title>登录验证码</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style type="text/css">
        body {
            margin: 0;
            padding: 0;
            background-color: #f5f5f5;
        }

        .container {
            max-width: 600px;
            margin: 0 auto;
            background-color: #ffffff;
            padding: 20px;
            box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
        }

        h1 {
            color: #ff6600;
            font-size: 24px;
            text-align: center;
            margin-top: 0;
            margin-bottom: 20px;
        }

        .code-container {
            display: flex;
            justify-content: center;
            align-items: center;
            margin-bottom: 30px;
        }

        .code {
            display: flex;
            flex-direction: row;
            justify-content: center;
            align-items: center;
            margin-right: 10px;
            box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
            padding: 10px;
            border-radius: 5px;
            font-size: 24px;
            font-weight: bold;
            color: #333333;
        }

        .btn-container {
            display: flex;
            justify-content: center;
            align-items: center;
            margin-bottom: 20px;
        }

        .btn {
            display: inline-block;
            padding: 10px 20px;
            background-color: #ff6600;
            color: #ffffff;
            border-radius: 5px;
            text-decoration: none;
            font-size: 18px;
            box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
            transition: background-color 0.3s ease;
        }

        .btn:hover {
            background-color: #e65c00;
        }

        .contact {
            text-align: center;
            font-size: 14px;
            color: #999999;
        }

        .contact a {
            color: #999999;
            text-decoration: none;
        }
    </style>
</head>
<body>
<div class="container">
    <h1>登录验证码</h1>


    <div class="code-container">
        <div class="code"><div style="border: solid 1px #ff6600; width: 40px; height: 40px; text-align: center; line-height: 40px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3); border-radius: 5px; font-size: 24px; font-weight: bold;">{{index .code 0}}</div></div>
        <div class="code"><div style="border: solid 1px #ff6600; width: 40px; height: 40px; text-align: center; line-height: 40px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3); border-radius: 5px; font-size: 24px; font-weight: bold;">{{index .code 1}}</div></div>
        <div class="code"><div style="border: solid 1px #ff6600; width: 40px; height: 40px; text-align: center; line-height: 40px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3); border-radius: 5px; font-size: 24px; font-weight: bold;">{{index .code 2}}</div></div>
        <div class="code"><div style="border: solid 1px #ff6600; width: 40px; height: 40px; text-align: center; line-height: 40px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3); border-radius: 5px; font-size: 24px; font-weight: bold;">{{index .code 3}}</div></div>
    </div>

    <div class="btn-container">
        <a href="#" class="btn" style="display: block; text-align: center;">立即登录</a>
    </div>
    <div class="contact">
        <p>如果您有任何问题,请联系客服:</p>
        <p><a href="tel:400-123-4567">400-123-4567</a></p>
    </div>
</div>
</body>
</html>

演示效果


PS:埋个坑,这个验证码很奇怪,并不是想要的,似乎是unicode码,应该如何转化正常的验证码?:smile:
如果你有更好的方案,下面评论吧~

讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!