Files
exchange_go/common/service/sysservice/authservice/captcha.go
2025-02-27 15:05:34 +08:00

558 lines
17 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 authservice
import (
"bytes"
"fmt"
"go-admin/common/const/rediskey"
"go-admin/common/helper"
statuscode "go-admin/common/status_code"
ext "go-admin/config"
"go-admin/pkg/cryptohelper/inttostring"
"io/ioutil"
"net/http"
"time"
"github.com/bytedance/sonic"
log "github.com/go-admin-team/go-admin-core/logger"
"go.uber.org/zap"
)
//
//// SendCaptchaBefore 发送验证码前之前检查
//func SendCaptchaBefore(orm *gorm.DB, auth sysmodel.SendCaptchaReq) int {
// var (
// user models.AdUser
// err error
// )
//
// // 除 注册、开启手机|邮箱验证、更改手机|邮箱验证 外,
// // 其余业务发送前都需验证账号是否存在
// if !(auth.BusinessType == int(businesstype.Register) ||
// auth.BusinessType == int(businesstype.OpenPhoneAuth) ||
// auth.BusinessType == int(businesstype.OpenEmailAuth) ||
// auth.IsNew == 1 && auth.BusinessType == int(businesstype.ChangePhoneAuth) ||
// auth.IsNew == 1 && auth.BusinessType == int(businesstype.ChangeEmailAuth)) {
//
// if auth.SendType == sysmodel.TSmsCode {
// // 手机
// user, err = aduserdb.GetUserByPhone(orm, auth.PhoneAreaCode, auth.Phone)
// if err != nil {
// return statuscode.ServerError
// }
// } else if auth.SendType == sysmodel.TEmailCode {
// // 邮箱
// user, err = aduserdb.GetUserByEmail(orm, auth.Email) //GetUser("useremail", auth.Email)
// if err != nil {
// return statuscode.ServerError
// }
// }
//
// // 账号不存在
// if user.Id == 0 {
// return statuscode.TheAccountIsNotRegistered
// }
// }
//
// // 除1 注册、2 登录、3 找回密码外,其余业务都需要登录(直接通过 uid 获取登录用户)
// if auth.BusinessType > 3 {
// user, err = aduserdb.GetUserById(orm, auth.Uid) //"id", auth.Uid)
// if err != nil {
// return statuscode.ServerError
// }
// }
//
// // 开启手机、开启邮箱 判断号码是否被占用(自己的号码判定为不占用)
// if auth.BusinessType == int(businesstype.OpenPhoneAuth) || auth.BusinessType == int(businesstype.OpenEmailAuth) {
// if auth.SendType == sysmodel.TSmsCode {
// // 手机
// user, err = aduserdb.GetUserByPhone(orm, auth.PhoneAreaCode, auth.Phone)
// if err != nil {
// return statuscode.ServerError
// }
//
// if user.Id != 0 && user.Id != auth.Uid {
// return statuscode.ThePhoneHasBeenBound
// }
// } else if auth.SendType == sysmodel.TEmailCode {
// // 邮箱
// user, err = aduserdb.GetUserByEmail(orm, auth.Email) //GetUser("useremail", auth.Email)
// if err != nil {
// return statuscode.ServerError
// }
//
// if user.Id != 0 && user.Id != auth.Uid {
// return statuscode.TheEmailHasBeenBound
// }
// }
// }
//
// // 更改手机、更改邮箱 仅判断新号码是否被占用(自己的号码判定为占用)
// if auth.IsNew == 1 &&
// (auth.BusinessType == int(businesstype.ChangePhoneAuth) || auth.BusinessType == int(businesstype.ChangeEmailAuth)) {
//
// if auth.SendType == sysmodel.TSmsCode {
// // 手机
// user, err = aduserdb.GetUserByPhone(orm, auth.PhoneAreaCode, auth.Phone)
// if err != nil {
// return statuscode.ServerError
// }
//
// if user.Id != 0 {
// return statuscode.ThePhoneHasBeenBound
// }
// } else if auth.SendType == sysmodel.TEmailCode {
// // 邮箱
// user, err = aduserdb.GetUserByEmail(orm, auth.Email) //GetUser("useremail", auth.Email)
// if err != nil {
// return statuscode.ServerError
// }
//
// if user.Id != 0 {
// return statuscode.TheEmailHasBeenBound
// }
// }
// }
//
// // 发送次数校验
// var sign interface{} = auth.Uid
// if sign == 0 {
// sign = auth.IP
// }
// key := fmt.Sprintf(rediskey.UserCaptchaSendFre, sign, auth.BusinessType)
//
// if countStr, err2 := helper.DefaultRedis.GetString(key); err2 == nil {
// count, _ := strconv.Atoi(countStr)
//
// if count >= 20 {
// return statuscode.CaptchaSendFrequencyExceedLimit
// }
// }
// /*if auth.BusinessType == int(enum.Register) {
// if count > 0 {
// return enum.CaptchaSendFrequencyExceedLimit
// }
// } else {
// if count >= 20 {
// return enum.CaptchaSendFrequencyExceedLimit
// }
// }*/
//
// return statuscode.OK
//}
//
//// IsRepeatSend 是否重复发送验证码
//func IsRepeatSend(orm *gorm.DB, receive string, businessType int) (bool, int) {
// verifyCode, err := aduserdb.IsRepeatSend(orm, receive, businessType)
// if err != nil {
// return false, 0
// }
//
// if verifyCode.Id != 0 {
// // 给 send 加时区,保持与当前时区一致
// send := verifyCode.SendTime
// send = time.Date(send.Year(), send.Month(), send.Day(), send.Hour(), send.Minute(), send.Second(), send.Nanosecond(), time.Local)
//
// // wait = send+60-now
// wait := send.Add(time.Minute).Sub(time.Now()).Seconds()
// return true, int(wait)
// }
//
// return false, 0
//}
//
//// 生成验证码
//func genVerifyCode() string {
// return fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000))
//}
//
////// 阿里云 - 发送手机验证码
////func SendALYSms(areaPhoneCode, phoneNumber string, businessType int) error {
////
//// var smsCode = genVerifyCode()
////
//// resp, err := smshelper.SendALYSmsCode(areaPhoneCode, phoneNumber, smsCode)
//// if err != nil {
//// return err
//// }
////
//// if *resp.Code != "OK" || *resp.Message != "OK" {
//// return errors.New(*resp.Message)
//// }
////
//// if err := RecordAuthCode(smsCode, phoneNumber, businessType); err != nil {
//// log.Error("record sms auth code", zap.Error(err))
//// return errors.New("this sms verification code is invalid")
//// }
////
//// return nil
////}
//
//// SendXDYSms 信达云 - 发送手机验证码
//func SendXDYSms(orm *gorm.DB, areaPhoneCode, phoneNumber string, businessType int) error {
// // SMS 模板
// msgBody := ""
// business := ""
// if businessType <= 102 {
// // <100 用通用模板
// msgBody = smstemplate.SmsTemplate_1_CN
// business = businesstype.BusinessTypeMapCN[businesstype.BusinessType(businessType)]
// if areaPhoneCode != "86" {
// msgBody = smstemplate.SmsTemplate_1_EN
// business = businesstype.BusinessTypeMapEN[businesstype.BusinessType(businessType)]
//
// }
// } else {
// //暂时只写中文 通用验证码
// msgBody = smstemplate.SmsTemplate_1_CN
// business = businesstype.BusinessTypeMapCN[businesstype.BusinessType(businessType)]
// }
//
// // 内容替换
// var smsCode = genVerifyCode()
// msgBody = strings.Replace(msgBody, "${business}", business, 1)
// msgBody = strings.Replace(msgBody, "${code}", smsCode, 1)
//
// // 发送验证码
// _, err := smshelper.SendDXYSmsCode(areaPhoneCode, phoneNumber, msgBody)
// if err != nil {
// return err
// }
//
// // 记录验证码
// if err := RecordAuthCode(orm, smsCode, phoneNumber, businessType); err != nil {
// log.Error("record sms auth code", zap.Error(err))
// return errors.New("this sms verification code is invalid")
// }
//
// return nil
//}
//
//// XindaCloud 回执报告
////func XindaCloudReceipt(ctx *fiber.Ctx) error {
//// var req interface{}
//// if err := ctx.BodyParser(&req); err != nil {
//// log.Error("BodyParser", zap.Error(err))
//// }
////
//// _ = redishelper.HMSet("XindaCloudReceipt", req)
////
//// return nil
////}
//
////// SendUserEmail 发送邮箱验证码
////func SendUserEmail(sendTo string, businessType int) error {
//// // 生成随机验证码
//// emailCode := genVerifyCode()
////
//// send := config.EmailSend{
//// EmailConfig: config.AppGlobalConfig.EmailConfig,
//// Subject: "邮箱验证码",
//// Content: "[HS] 您的邮箱验证码为: " + emailCode,
//// To: sendTo,
//// }
////
//// if _, ok := emailhelper.SendEmail(send); ok {
//// if err := RecordAuthCode(emailCode, sendTo, businessType); err != nil {
//// log.Error("record email auth code", zap.Error(err))
//// return errors.New("this email verification code is invalid")
//// }
////
//// return nil
//// }
////
//// return errors.New("email send failed")
////}
//
//func MailJetSend(orm *gorm.DB, receive string, businessType int) error {
// // 生成随机验证码
// code := genVerifyCode()
//
// if err := emailhelper.MailJetSend(receive, code); err != nil {
// return err
// }
//
// if err := RecordAuthCode(orm, code, receive, businessType); err != nil {
// log.Error("record email auth code", zap.Error(err))
// return errors.New("record email auth code failed")
// }
//
// return nil
//}
//
//// RecordAuthCode 记录验证码
//func RecordAuthCode(orm *gorm.DB, authCode, receive string, businessType int) error {
// var (
// sendTime = time.Now()
// expireTime = sendTime.Add(sysmodel.CodeValidTime)
// )
//
// var authCodeInfo = &models.AdVerifyCode{
// Phone: receive,
// Code: authCode,
// SendTime: sendTime,
// ValidTimeLen: int(sysmodel.CodeValidTime.Seconds()),
// VerifyTime: expireTime,
// VerifyFlag: 0,
// BusinessType: businessType,
// }
//
// return orm.Model(authCodeInfo).Create(authCodeInfo).Error
// // return dbhelper.MasterPgdb.Insert("ad_verifycode", authCodeInfo)
//}
//
//// // CheckCaptcha 校验验证码
//// func CheckCaptcha(cc sysmodel.CheckCaptcha, tx *sqlx.Tx) error {
//// // TODO 免检测验证码
//// if cc.Captcha == "123456" {
//// return nil
//// }
//// if cc.Captcha == config.AppGlobalConfig.OpenVerifyCode {
//// return nil
//// }
////
//// return aduserdb.UpdateVerifyCode(cc, tx)
//// }
//
//// CheckOpenVerifyCode 校验验证码是否免检测验证码,true免检测验证码false需要检测验证码
//func CheckOpenVerifyCode(captcha string) bool {
// // TODO 免检测验证码
// //if captcha == "123456" {
// // return true
// //}
//
// if captcha == config.ExtConfig.OpenVerifyCode {
// return true
// }
// return false
//}
//
//// CheckPhoneOrEmailCaptcha 手机或者邮箱验证码检测
//func CheckPhoneOrEmailCaptcha(orm *gorm.DB, phone sysmodel.CheckCaptcha) int {
// return CheckCaptchaNew(orm, phone, sysmodel.CheckCaptcha{}, "", "")
//}
//
//// CheckCaptchaNew 校验验证码,1.phone手机验证2.email邮箱验证3.googleAuth谷歌验证也可以只传一个结构体比如phone验证一个无需指定是手机或者邮箱
//func CheckCaptchaNew(orm *gorm.DB, phone sysmodel.CheckCaptcha, email sysmodel.CheckCaptcha, googleSecret, googleCaptcha string) int {
// // TODO 免检测验证码
//
// var phoneCode models.AdVerifyCode
// if len(phone.Captcha) > 0 && !CheckOpenVerifyCode(phone.Captcha) {
// // 校验手机号码发送的验证码
// phoneCode = aduserdb.GetVerifyCode(orm, phone)
// if phoneCode.Id == 0 {
// return statuscode.PhoneCaptchaInvalid
// }
// }
//
// var emailCode models.AdVerifyCode
// if len(email.Captcha) > 0 && !CheckOpenVerifyCode(email.Captcha) {
// // 校验邮箱号码发送的验证码
// emailCode = aduserdb.GetVerifyCode(orm, email)
// if emailCode.Id == 0 {
// return statuscode.EmailCaptchaInvalid
// }
// }
// if len(googleCaptcha) > 0 {
// // 校验谷歌的验证码
// auth := googleauthhelper.VerifyCode(googleSecret, utility.StringAsInt32(googleCaptcha))
// if !auth {
// return statuscode.GoogleCaptchaInvalid
// }
// }
// // 没手机+邮箱验证应该只验证谷歌到这里就返回ok
// if phoneCode.Id == 0 && emailCode.Id == 0 {
// return statuscode.OK
// }
//
// var transCode int
//
// // 更新手机+邮箱验证码的数据状态
// err := orm.Transaction(func(tx *gorm.DB) (err error) {
//
// if phoneCode.Id > 0 {
// err = aduserdb.UpdateVerifyFlag(orm, phoneCode.Id)
// if err != nil {
// // _ = tx.Rollback()
// transCode = statuscode.PhoneCaptchaInvalid
//
// return err
// }
// }
// if emailCode.Id > 0 {
// err = aduserdb.UpdateVerifyFlag(orm, emailCode.Id)
// if err != nil {
// // _ = tx.Rollback()
// transCode = statuscode.EmailCaptchaInvalid
//
// return err
// }
// }
//
// return nil
// })
// // tx, err := dbhelper.MasterPgdb.Beginx()
// // if err != nil {
// // log.Error("Begin", zap.Error(err))
// // return statuscode.ServerError
// // }
// // if phoneCode.Id > 0 {
// // err = aduserdb.UpdateVerifyFlag(orm, phoneCode.Id)
// // if err != nil {
// // _ = tx.Rollback()
// // return statuscode.PhoneCaptchaInvalid
// // }
// // }
// // if emailCode.Id > 0 {
// // err = aduserdb.UpdateVerifyFlag(orm, emailCode.Id)
// // if err != nil {
// // _ = tx.Rollback()
// // return statuscode.EmailCaptchaInvalid
// // }
// // }
// // _ = tx.Commit()
//
// if err != nil {
// return transCode
// }
//
// return statuscode.OK
//}
//
//// CheckCredentials 校验凭证
//func CheckCredentials(req sysmodel.Credential, credentials string) bool {
// // 解析得到 Credentials Json
// CreJ := aeshelper.Decrypt(credentials, codeVerifySuccess)
// if len(CreJ) <= 0 {
// log.Error("credentials error")
// return false
// }
//
// Cre := sysmodel.Credential{}
// _ = sonic.Unmarshal([]byte(CreJ), &Cre)
//
// // 凭证最长有效时间2分钟
// var maxValidTime = int64(2 * time.Minute.Seconds())
//
// // 逐一比对凭证字段
// if Cre.BusinessType != req.BusinessType {
// log.Error(fmt.Sprintf("凭证业务类型有误, BusinesType: %d credentialsBusinesType %d", req.BusinessType, Cre.BusinessType))
// return false
// }
//
// if Cre.UserID != req.UserID {
// log.Error(fmt.Sprintf("凭证用户ID有误, UserID: %d credentialsUserID %d", req.UserID, Cre.UserID))
// return false
// }
//
// if len(Cre.Phone) > 0 && Cre.Phone != req.Phone {
// log.Error(fmt.Sprintf("凭证手机有误, Phone: %s credentialsPhone %s", req.Phone, Cre.Phone))
// return false
// }
//
// if len(Cre.Email) > 0 && Cre.Email != req.Email {
// log.Error(fmt.Sprintf("凭证邮箱有误, Email: %s credentialsEmail %s", req.Email, Cre.Email))
// return false
// }
//
// if time.Now().Unix() > Cre.Time+maxValidTime {
// log.Error(fmt.Sprintf("凭证已过期, 当前时间: %d 凭证过期时间 %d", time.Now().Unix(), Cre.Time+maxValidTime))
// return false
// }
//
// return true
//}
// smsType 0-注册 1-重置密码
func SendGoToneSms(phone, area string, smsType int) int {
//smsCode =
smsString := inttostring.GenerateRandomSmsString(6)
defer func() {
// 使用 recover 来捕获 panic避免 goroutine 导致程序崩溃
if r := recover(); r != nil {
log.Error("SendRegisterEmail Error:", r)
}
}()
var key string
var registerKey string
switch smsType {
case 0:
registerKey = fmt.Sprintf("mobile-%s-register", phone)
key = fmt.Sprintf(rediskey.PCRegisterMobile, phone)
case 1:
registerKey = fmt.Sprintf("mobile-%s-resetpwd", phone)
key = fmt.Sprintf(rediskey.PCResetPwdMobile, phone)
default:
return statuscode.GoToneSmsTypeErr
}
get := helper.DefaultRedis.Get(registerKey)
if get.Val() != "" { //说明邮箱操作频繁
return statuscode.GoToneSmsOrderTooOften
}
if err := helper.DefaultRedis.SetStringExpire(key, smsString, time.Second*300); err != nil {
log.Error("sendEmail setRedis Error:", zap.Error(err))
return statuscode.ServerError
}
//ext.ExtConfig.GoToneSmsConfig
// SmsRequest 用于构建发送短信请求的结构体
type SmsRequest struct {
Recipient string `json:"recipient"` // 收件人电话号码
Message string `json:"message"` // 短信内容
SenderId string `json:"sender_id"` // 发送者名称
Type string `json:"type"`
}
// 创建请求数据
smsRequest := SmsRequest{
Recipient: "+" + area + phone,
SenderId: ext.ExtConfig.GoToneSmsConfig.SenderId,
Message: fmt.Sprintf("欢迎使用 GoTone SMS高速稳定地发送短信至中国大陆及全球用户体验验证码%s。如非本人操作请忽略此信息", smsString),
Type: "plain",
}
// 将请求数据编码为 JSON
requestBody, err := sonic.Marshal(smsRequest)
if err != nil {
log.Error("GoToneSms requestBody Error:", err)
return statuscode.ServerError
}
// 创建 HTTP 请求
req, err := http.NewRequest("POST", ext.ExtConfig.GoToneSmsConfig.APIEndpoint, bytes.NewBuffer(requestBody))
if err != nil {
log.Error("GoToneSms http.NewRequest Error:", err)
return statuscode.ServerError
}
// 设置请求头
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+ext.ExtConfig.GoToneSmsConfig.Authorization) // 使用 API 密钥进行身份验证
// 创建 HTTP 客户端并发送请求
client := &http.Client{
Timeout: 10 * time.Second, // 设置请求超时时间
}
resp, err := client.Do(req)
fmt.Println("resp:", resp)
if err != nil {
log.Error("GoToneSms do NewRequest Error:", err)
return statuscode.CaptchaFailInSend
}
defer resp.Body.Close()
// 检查响应状态
if resp.StatusCode != http.StatusOK {
log.Error("GoToneSms do NewRequest Error:", err)
return statuscode.CaptchaFailInSend
}
// 读取响应体
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error("读取响应体失败:", err)
fmt.Printf("响应体: %s", string(body))
return statuscode.CaptchaFailInSend
//return fmt.Errorf("读取响应体失败: %v", err)
}
// 打印响应内容(调试用)
//记录短信发送操作
helper.DefaultRedis.SetStringExpire(registerKey, "1", time.Second*60)
return statuscode.OK
}