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 }