geekai/api/handler/verify_handler.go

151 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handler
import (
"chatplus/core"
"chatplus/core/types"
"chatplus/service"
"chatplus/store"
"chatplus/utils"
"chatplus/utils/resp"
"fmt"
"time"
"github.com/gin-gonic/gin"
)
// 生成验证的控制器
type VerifyHandler struct {
BaseHandler
sms *service.AliYunSmsService
db *store.LevelDB
}
const TokenStorePrefix = "/verify/tokens/"
const CodeStorePrefix = "/verify/codes/"
const MobileStatPrefix = "/verify/stats/"
func NewVerifyHandler(app *core.AppServer, sms *service.AliYunSmsService, db *store.LevelDB) *VerifyHandler {
handler := &VerifyHandler{sms: sms, db: db}
handler.App = app
return handler
}
type VerifyToken struct {
Token string
Timestamp int64
}
// CodeStats 验证码发送统计
type CodeStats struct {
Mobile string
Count uint
Time int64
}
// Token 生成自验证 token
func (h *VerifyHandler) Token(c *gin.Context) {
// 如果不是通过浏览器访问,则返回错误的 token
if c.GetHeader("Sec-Fetch-Mode") != "cors" {
token := fmt.Sprintf("%s:%d", utils.RandString(32), time.Now().Unix())
encrypt, err := utils.AesEncrypt(h.App.Config.AesEncryptKey, []byte(token))
if err != nil {
resp.ERROR(c, "Token 加密出错")
return
}
resp.SUCCESS(c, encrypt)
return
}
token := VerifyToken{
Token: utils.RandString(32),
Timestamp: time.Now().Unix(),
}
json := utils.JsonEncode(token)
encrypt, err := utils.AesEncrypt(h.App.Config.AesEncryptKey, []byte(json))
if err != nil {
resp.ERROR(c, "Token 加密出错")
return
}
err = h.db.Put(TokenStorePrefix+token.Token, token)
if err != nil {
resp.ERROR(c, "Token 存储失败")
return
}
resp.SUCCESS(c, encrypt)
}
// SendMsg 发送验证码短信
func (h *VerifyHandler) SendMsg(c *gin.Context) {
var data struct {
Mobile string `json:"mobile"`
Token string `json:"token"`
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
decrypt, err := utils.AesDecrypt(h.App.Config.AesEncryptKey, data.Token)
if err != nil {
resp.ERROR(c, "Token 解密失败")
return
}
var token VerifyToken
err = utils.JsonDecode(string(decrypt), &token)
if err != nil {
resp.ERROR(c, "Token 解码失败")
return
}
if time.Now().Unix()-token.Timestamp > 30 {
resp.ERROR(c, "Token 已过期,请刷新页面重试")
return
}
// 验证当前手机号发送次数24 小时内相同手机号只允许发送 2 次
var stat CodeStats
err = h.db.Get(MobileStatPrefix+data.Mobile, &stat)
if err != nil {
logger.Error(err)
stat = CodeStats{
Mobile: data.Mobile,
Count: 0,
Time: time.Now().Unix(),
}
} else if stat.Count == 2 {
if time.Now().Unix()-stat.Time > 86400 {
stat.Count = 0
stat.Time = time.Now().Unix()
} else {
resp.ERROR(c, "触发流量预警,请 24 小时后再操作!")
return
}
}
code := utils.RandomNumber(6)
err = h.sms.SendVerifyCode(data.Mobile, code)
if err != nil {
resp.ERROR(c, err.Error())
return
}
// 每个 token 用完一次立即失效
_ = h.db.Delete(TokenStorePrefix + token.Token)
// 存储验证码,等待后面注册验证
err = h.db.Put(CodeStorePrefix+data.Mobile, code)
if err != nil {
resp.ERROR(c, "验证码保存失败")
return
}
// 更新发送次数
stat.Count = stat.Count + 1
_ = h.db.Put(MobileStatPrefix+data.Mobile, stat)
logger.Infof("%+v", stat)
resp.SUCCESS(c)
}