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}} 到模板中渲染后即可发送

代码已被折叠,点此展开

演示效果#


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