面包店
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 

262 行
6.6 KiB

  1. package mob
  2. import (
  3. "applet/app/db"
  4. "applet/app/lib/sms"
  5. "applet/app/lib/zhimeng"
  6. "applet/app/utils"
  7. "applet/app/utils/logx"
  8. "bytes"
  9. "crypto/cipher"
  10. "crypto/des"
  11. "crypto/md5"
  12. "encoding/base64"
  13. "encoding/hex"
  14. "encoding/json"
  15. "errors"
  16. "fmt"
  17. "io/ioutil"
  18. "net/http"
  19. "sort"
  20. "time"
  21. "github.com/gin-gonic/gin"
  22. "github.com/tidwall/gjson"
  23. )
  24. const base string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
  25. // Mob is mob sdk
  26. var Mob *SDK
  27. // MobMap is 每个站长都要有自己的mob 对象
  28. var MobMap map[string]*SDK
  29. //Init 初始化
  30. func Init() {
  31. // 后续可能要传请求的上下文来获取对应的配置
  32. // mob 目前都是我们来管理每个站长的app 所以使用template 库
  33. //fmt.Println("Mob SDK init ....")
  34. ch := make(chan struct{}) // 只是做信号标志的话 空struct 更省点资源
  35. MobMap = make(map[string]*SDK)
  36. // 初始化
  37. for k, e := range db.DBs {
  38. m, err := db.SysCfgGetOne(e, "third_app_push_set")
  39. if err != nil {
  40. logx.Warn(err)
  41. fmt.Println(k + ":init mob err")
  42. continue
  43. }
  44. key := gjson.Get(m.Val, "mobAppKey").String()
  45. secret := gjson.Get(m.Val, "mobAppSecret").String()
  46. if key == "" || secret == "" {
  47. fmt.Println(k + ":mob no config")
  48. continue
  49. }
  50. // fmt.Println(k, key, secret)
  51. mob := new(SDK)
  52. mob.AppKey = key
  53. mob.AppSecret = secret
  54. MobMap[k] = mob
  55. fmt.Println(k + ":mob config success")
  56. }
  57. go func() {
  58. ch <- struct{}{}
  59. }()
  60. // 定时任务
  61. go func(MobMap map[string]*SDK, ch chan struct{}) {
  62. <-ch
  63. ticker := time.NewTicker(time.Duration(time.Second * 15))
  64. //每 15s 一次更新一次mob 配置
  65. for range ticker.C {
  66. for k, e := range db.DBs {
  67. if err := e.Ping(); err != nil {
  68. logx.Info(err)
  69. continue
  70. }
  71. m, err := db.SysCfgGetOne(e, "third_app_push_set")
  72. if err != nil {
  73. logx.Warn(err)
  74. fmt.Println(k + ":init mob err")
  75. continue
  76. }
  77. key := gjson.Get(m.Val, "mobAppKey").String()
  78. secret := gjson.Get(m.Val, "mobAppSecret").String()
  79. if key == "" || secret == "" {
  80. fmt.Println(k + ":mob no config")
  81. continue
  82. }
  83. // fmt.Println(k, key, secret)
  84. mob := new(SDK)
  85. mob.AppKey = key
  86. mob.AppSecret = secret
  87. MobMap[k] = mob
  88. // fmt.Println(k + ":mob config success")
  89. }
  90. }
  91. }(MobMap, ch)
  92. }
  93. // GetMobSDK is 获取mob 的sdk
  94. func GetMobSDK(mid string) (*SDK, error) {
  95. selectDB := db.DBs[mid]
  96. m, err := db.SysCfgGetOne(selectDB, "third_app_push_set")
  97. if err != nil {
  98. return nil, err
  99. }
  100. key := gjson.Get(m.Val, "mobAppKey").String()
  101. secret := gjson.Get(m.Val, "mobAppSecret").String()
  102. if key == "" || secret == "" {
  103. return nil, fmt.Errorf("%s mob not config", mid)
  104. }
  105. return &SDK{AppKey: key, AppSecret: secret}, nil
  106. }
  107. // SDK is mob_push 的sdk
  108. type SDK struct {
  109. AppKey string
  110. AppSecret string
  111. }
  112. //MobFreeLogin is 秒验
  113. func (s *SDK) MobFreeLogin(args map[string]interface{}) (string, error) {
  114. var url string = "http://identify.verify.mob.com/auth/auth/sdkClientFreeLogin"
  115. // https://www.mob.com/wiki/detailed/?wiki=miaoyan_for_fuwuduan_mianmifuwuduanjieru&id=78
  116. //加appkey
  117. args["appkey"] = s.AppKey
  118. //加签名
  119. args["sign"] = generateSign(args, s.AppSecret)
  120. b, err := json.Marshal(args)
  121. if err != nil {
  122. return "", logx.Warn(err)
  123. }
  124. // 发送请求
  125. respBody, err := httpPostBody(url, b)
  126. if err != nil {
  127. return "", logx.Warn(err)
  128. }
  129. // 反序列化
  130. ret := struct {
  131. Status int `json:"status"`
  132. Error string `json:"error"`
  133. Res interface{} `json:"res"`
  134. }{}
  135. // 要拿 ret 里面 Res 再解密
  136. if err := json.Unmarshal(respBody, &ret); err != nil {
  137. return "", logx.Warn(err)
  138. }
  139. //fmt.Println(ret)
  140. // ret里面的Res 反序列化为结构体
  141. res := struct {
  142. IsValid int `json:"isValid"`
  143. Phone string `json:"phone"`
  144. }{}
  145. // 判断是否返回正确 状态码
  146. if ret.Status == 200 {
  147. decode, _ := base64Decode([]byte(ret.Res.(string)))
  148. decr, _ := desDecrypt(decode, []byte(s.AppSecret)[0:8])
  149. if err := json.Unmarshal(decr, &res); err != nil {
  150. return "", logx.Warn(err)
  151. }
  152. }
  153. // 有效则拿出res 里的电话号码
  154. if res.IsValid == 1 {
  155. return res.Phone, nil
  156. }
  157. // Status 不等于200 则返回空
  158. return "", fmt.Errorf("Mob error , status code %v ", ret.Status)
  159. }
  160. // MobSMS is mob 的短信验证
  161. func (s *SDK) MobSMS(c *gin.Context, args map[string]interface{}) (bool, error) {
  162. // mob 的短信验证
  163. // https://www.mob.com/wiki/detailed/?wiki=SMSSDK_for_yanzhengmafuwuduanxiaoyanjiekou&id=23
  164. url := "https://webapi.sms.mob.com/sms/verify"
  165. //加appkey
  166. args["appkey"] = s.AppKey
  167. //fmt.Println(args)
  168. // 发送请求
  169. respBody, err := utils.CurlPost(url, args, nil)
  170. if err != nil {
  171. return false, logx.Warn(err)
  172. }
  173. //fmt.Println(string(respBody))
  174. code := gjson.GetBytes(respBody, "status").Int()
  175. if code != 200 {
  176. return false, nil
  177. }
  178. if code == 468 {
  179. return false, errors.New("验证码错误")
  180. }
  181. // TODO 成功后扣费暂时先接旧智盟
  182. sdk, err := sms.NewZhimengSMS(c).SelectFunction("deduction_doing").WithSMSArgs(map[string]interface{}{
  183. "mobile": args["phone"],
  184. "getmsg": "1",
  185. }).Result()
  186. if err != nil {
  187. return false, logx.Warn(err)
  188. }
  189. zr := sdk.ToInterface().(string)
  190. if zr == "1" {
  191. logx.Infof("旧智盟扣费成功 appkey %s", zhimeng.SMS_APP_KEY)
  192. }
  193. return true, nil
  194. }
  195. func pkcs5UnPadding(origData []byte) []byte {
  196. length := len(origData)
  197. // 去掉最后一个字节 unpadding 次
  198. unpadding := int(origData[length-1])
  199. return origData[:(length - unpadding)]
  200. }
  201. func desDecrypt(crypted, key []byte) ([]byte, error) {
  202. block, err := des.NewCipher(key)
  203. if err != nil {
  204. return nil, err
  205. }
  206. blockMode := cipher.NewCBCDecrypter(block, []byte("00000000"))
  207. origData := make([]byte, len(crypted))
  208. // origData := crypted
  209. blockMode.CryptBlocks(origData, crypted)
  210. origData = pkcs5UnPadding(origData)
  211. // origData = ZeroUnPadding(origData)
  212. return origData, nil
  213. }
  214. func base64Decode(src []byte) ([]byte, error) {
  215. var coder *base64.Encoding
  216. coder = base64.NewEncoding(base)
  217. return coder.DecodeString(string(src))
  218. }
  219. func httpPostBody(url string, msg []byte) ([]byte, error) {
  220. resp, err := http.Post(url, "application/json;charset=utf-8", bytes.NewBuffer(msg))
  221. if err != nil {
  222. return []byte(""), err
  223. }
  224. defer resp.Body.Close()
  225. body, err := ioutil.ReadAll(resp.Body)
  226. return body, err
  227. }
  228. func generateSign(request map[string]interface{}, secret string) string {
  229. ret := ""
  230. var keys []string
  231. for k := range request {
  232. keys = append(keys, k)
  233. }
  234. sort.Strings(keys)
  235. for _, k := range keys {
  236. ret = ret + fmt.Sprintf("%v=%v&", k, request[k])
  237. }
  238. ret = ret[:len(ret)-1] + secret
  239. md5Ctx := md5.New()
  240. md5Ctx.Write([]byte(ret))
  241. cipherStr := md5Ctx.Sum(nil)
  242. return hex.EncodeToString(cipherStr)
  243. }