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 码,应该如何转化正常的验证码?
如果你有更好的方案,下面评论吧~
推荐文章: