package mob import ( "applet/app/db" "applet/app/lib/sms" "applet/app/lib/zhimeng" "applet/app/utils" "applet/app/utils/logx" "bytes" "crypto/cipher" "crypto/des" "crypto/md5" "encoding/base64" "encoding/hex" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "sort" "time" "github.com/gin-gonic/gin" "github.com/tidwall/gjson" ) const base string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" // Mob is mob sdk var Mob *SDK // MobMap is 每个站长都要有自己的mob 对象 var MobMap map[string]*SDK //Init 初始化 func Init() { // 后续可能要传请求的上下文来获取对应的配置 // mob 目前都是我们来管理每个站长的app 所以使用template 库 //fmt.Println("Mob SDK init ....") ch := make(chan struct{}) // 只是做信号标志的话 空struct 更省点资源 MobMap = make(map[string]*SDK) // 初始化 for k, e := range db.DBs { m, err := db.SysCfgGetOne(e, "third_app_push_set") if err != nil { logx.Warn(err) fmt.Println(k + ":init mob err") continue } key := gjson.Get(m.Val, "mobAppKey").String() secret := gjson.Get(m.Val, "mobAppSecret").String() if key == "" || secret == "" { fmt.Println(k + ":mob no config") continue } // fmt.Println(k, key, secret) mob := new(SDK) mob.AppKey = key mob.AppSecret = secret MobMap[k] = mob fmt.Println(k + ":mob config success") } go func() { ch <- struct{}{} }() // 定时任务 go func(MobMap map[string]*SDK, ch chan struct{}) { <-ch ticker := time.NewTicker(time.Duration(time.Second * 15)) //每 15s 一次更新一次mob 配置 for range ticker.C { for k, e := range db.DBs { if err := e.Ping(); err != nil { logx.Info(err) continue } m, err := db.SysCfgGetOne(e, "third_app_push_set") if err != nil { logx.Warn(err) fmt.Println(k + ":init mob err") continue } key := gjson.Get(m.Val, "mobAppKey").String() secret := gjson.Get(m.Val, "mobAppSecret").String() if key == "" || secret == "" { fmt.Println(k + ":mob no config") continue } // fmt.Println(k, key, secret) mob := new(SDK) mob.AppKey = key mob.AppSecret = secret MobMap[k] = mob // fmt.Println(k + ":mob config success") } } }(MobMap, ch) } // GetMobSDK is 获取mob 的sdk func GetMobSDK(mid string) (*SDK, error) { selectDB := db.DBs[mid] m, err := db.SysCfgGetOne(selectDB, "third_app_push_set") if err != nil { return nil, err } key := gjson.Get(m.Val, "mobAppKey").String() secret := gjson.Get(m.Val, "mobAppSecret").String() if key == "" || secret == "" { return nil, fmt.Errorf("%s mob not config", mid) } return &SDK{AppKey: key, AppSecret: secret}, nil } // SDK is mob_push 的sdk type SDK struct { AppKey string AppSecret string } //MobFreeLogin is 秒验 func (s *SDK) MobFreeLogin(args map[string]interface{}) (string, error) { var url string = "http://identify.verify.mob.com/auth/auth/sdkClientFreeLogin" // https://www.mob.com/wiki/detailed/?wiki=miaoyan_for_fuwuduan_mianmifuwuduanjieru&id=78 //加appkey args["appkey"] = s.AppKey //加签名 args["sign"] = generateSign(args, s.AppSecret) b, err := json.Marshal(args) if err != nil { return "", logx.Warn(err) } // 发送请求 respBody, err := httpPostBody(url, b) if err != nil { return "", logx.Warn(err) } // 反序列化 ret := struct { Status int `json:"status"` Error string `json:"error"` Res interface{} `json:"res"` }{} // 要拿 ret 里面 Res 再解密 if err := json.Unmarshal(respBody, &ret); err != nil { return "", logx.Warn(err) } //fmt.Println(ret) // ret里面的Res 反序列化为结构体 res := struct { IsValid int `json:"isValid"` Phone string `json:"phone"` }{} // 判断是否返回正确 状态码 if ret.Status == 200 { decode, _ := base64Decode([]byte(ret.Res.(string))) decr, _ := desDecrypt(decode, []byte(s.AppSecret)[0:8]) if err := json.Unmarshal(decr, &res); err != nil { return "", logx.Warn(err) } } // 有效则拿出res 里的电话号码 if res.IsValid == 1 { return res.Phone, nil } // Status 不等于200 则返回空 return "", fmt.Errorf("Mob error , status code %v ", ret.Status) } // MobSMS is mob 的短信验证 func (s *SDK) MobSMS(c *gin.Context, args map[string]interface{}) (bool, error) { // mob 的短信验证 // https://www.mob.com/wiki/detailed/?wiki=SMSSDK_for_yanzhengmafuwuduanxiaoyanjiekou&id=23 url := "https://webapi.sms.mob.com/sms/verify" //加appkey args["appkey"] = s.AppKey //fmt.Println(args) // 发送请求 respBody, err := utils.CurlPost(url, args, nil) if err != nil { return false, logx.Warn(err) } //fmt.Println(string(respBody)) code := gjson.GetBytes(respBody, "status").Int() if code != 200 { return false, nil } if code == 468 { return false, errors.New("验证码错误") } // TODO 成功后扣费暂时先接旧智盟 sdk, err := sms.NewZhimengSMS(c).SelectFunction("deduction_doing").WithSMSArgs(map[string]interface{}{ "mobile": args["phone"], "getmsg": "1", }).Result() if err != nil { return false, logx.Warn(err) } zr := sdk.ToInterface().(string) if zr == "1" { logx.Infof("旧智盟扣费成功 appkey %s", zhimeng.SMS_APP_KEY) } return true, nil } func pkcs5UnPadding(origData []byte) []byte { length := len(origData) // 去掉最后一个字节 unpadding 次 unpadding := int(origData[length-1]) return origData[:(length - unpadding)] } func desDecrypt(crypted, key []byte) ([]byte, error) { block, err := des.NewCipher(key) if err != nil { return nil, err } blockMode := cipher.NewCBCDecrypter(block, []byte("00000000")) origData := make([]byte, len(crypted)) // origData := crypted blockMode.CryptBlocks(origData, crypted) origData = pkcs5UnPadding(origData) // origData = ZeroUnPadding(origData) return origData, nil } func base64Decode(src []byte) ([]byte, error) { var coder *base64.Encoding coder = base64.NewEncoding(base) return coder.DecodeString(string(src)) } func httpPostBody(url string, msg []byte) ([]byte, error) { resp, err := http.Post(url, "application/json;charset=utf-8", bytes.NewBuffer(msg)) if err != nil { return []byte(""), err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) return body, err } func generateSign(request map[string]interface{}, secret string) string { ret := "" var keys []string for k := range request { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { ret = ret + fmt.Sprintf("%v=%v&", k, request[k]) } ret = ret[:len(ret)-1] + secret md5Ctx := md5.New() md5Ctx.Write([]byte(ret)) cipherStr := md5Ctx.Sum(nil) return hex.EncodeToString(cipherStr) }