文章目录
邮箱登陆验证
流程:
-
接收用户请求后生成随机验证码,并将验证码存入Redis中,并设置TTL
-
通过gomail发送验证码给用户邮箱
-
接收用户输入的验证码,与Redis中存放的验证码进行比对
生成随机验证码
随机种子通过 time.Now().UnixNano()
进行设置,以确保对于同一个用户每次请求都使用不同的种子。然后,定义了一个包含数字字符的 letters
切片。每次通过 rand.Intn(len(letters))
随机选择 letters
切片中的一个字符,并将其存储在 b
中。最后,将 b
转换为字符串并返回。
go
import (
"math/rand"
"time"
)
func GenerateRandomCode(length int) string {
rand.Seed(time.Now().UnixNano())
var letters = []rune("0123456789")
b := make([]rune, length)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
向用户邮箱发送验证码
- 这里使用了gomail进行发送邮箱验证码。gomail package - gopkg.in/gomail.v2 - Go Packages
- 此处密码为授权码而非登陆密码,以qq为例:需要在邮箱设置->账号安全->POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务处开启服务,然后获取授权码
Host
:SMTP 服务器的主机地址。Port
:SMTP 服务器的端口号。Username
:SMTP 服务器的邮箱账号。Password
:SMTP 服务器的密码。
yaml
# qq邮箱
host: smtp.qq.com
port: 587
username: ********@qq.com
password: ********
go
// 发送邮箱验证码
func EmailSendCode(ctx context.Context, email string) (code string, err error) {
// 生成6位随机验证码
code = vcode.GenerateRandomCode(6)
m := gomail.NewMessage()
m.SetHeader("From", cfg.username)
m.SetHeader("To", email)
m.SetHeader("Subject", "验证码")
msg := fmt.Sprintf("您的验证码为: %s", code)
m.SetBody("text/html", msg)
d := gomail.NewDialer(cfg.host, cfg.port, cfg.username, cfg.password)
err = d.DialAndSend(m)
return
}
接口
Goframe 框架
获取验证码
如果发送验证码成功,将验证码存储到 Redis 中。这里 使用SETEX
命令设置了一个键值对,键以邮箱地址为后缀,值是验证码,过期时间设置为 120 秒(2 分钟)。(Goframe框架)
go
func (c *ControllerV1) SendCode(ctx context.Context, req *v1.SendCodeReq) (res *v1.SendCodeRes, err error) {
res = &v1.SendCodeRes{}
code, err := email.EmailSendCode(ctx, req.Email)
if err != nil {
return
}
// 将验证码存入Redis中,并设置TTL
_, err = g.Redis().Do(ctx, "SETEX", fmt.Sprintf("code.%s", req.Email), 120, code)
if err != nil {
return
}
res.States = true
return
}
校验验证码
检查请求中是否包含验证码。如果验证码非空,则从 Redis 中获取之前发送给该邮箱的验证码,并与请求中的验证码进行比较。如果验证码不匹配,则返回一个错误。验证码正确则查询用户信息,用用户的唯一标识符获取 JWT 令牌。最后,将 JWT 令牌包装在响应对象中返回。
go
func (c *ControllerV1) Login(ctx context.Context, req *v1.LoginReq) (res *v1.LoginRes, err error) {
user := entity.User{
Email: req.Email,
}
if req.VerificationCode != "" {
search, _ := g.Redis().Do(ctx, "GET", fmt.Sprintf("code.%s", req.Email))
code := search.String()
if code != req.VerificationCode {
return res, gerror.New("验证码错误或未发送")
}
}
// 查询用户信息
search, err := service.User().UserInfo(ctx,user)
if err != nil {
return
}
// 保存jwt令牌
token, _ := service.JwtStorage().GetJwtAndSave(ctx, search.Id)
res = &v1.LoginRes{
Token: token,
}
return
}