Browse Source

更换发送短信api

Ohio-HYF 2 years ago
parent
commit
9d0d324266
3 changed files with 125 additions and 18 deletions
  1. 1 0
      go.mod
  2. 2 0
      go.sum
  3. 122 18
      service/send_code.go

+ 1 - 0
go.mod

@@ -19,6 +19,7 @@ require (
 	github.com/mattn/go-isatty v0.0.14 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/robfig/cron/v3 v3.0.1
+	github.com/satori/go.uuid v1.2.0
 	github.com/sirupsen/logrus v1.8.1
 	github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2
 	github.com/swaggo/gin-swagger v1.4.1

+ 2 - 0
go.sum

@@ -157,6 +157,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
 github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
 github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
 github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=

+ 122 - 18
service/send_code.go

@@ -4,23 +4,34 @@ import (
 	// "apig-sdk/go/core"
 	"bytes"
 	"context"
+	"crypto/sha256"
+	"crypto/tls"
+	"encoding/base64"
 	"errors"
 	"fmt"
 	"io/ioutil"
 	"math/rand"
 	"net/http"
+	"net/url"
+	"strings"
 	"time"
 	"youngee_b_api/consts"
-	"youngee_b_api/core"
 	"youngee_b_api/db"
 	"youngee_b_api/model/system_model"
 	"youngee_b_api/redis"
 
+	uuid "github.com/satori/go.uuid"
 	"github.com/sirupsen/logrus"
 )
 
 var SendCode *sendCode
 
+//无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值
+const WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\""
+
+//无需修改,用于格式化鉴权头域,给"Authorization"参数赋值
+const AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\""
+
 func SendCodeInit(config *system_model.Session) {
 	sendCode := new(sendCode)
 	sendCode.sessionTTL = time.Duration(config.TTL) * time.Minute
@@ -62,29 +73,122 @@ func (s *sendCode) GetEmailByPhone(ctx context.Context, phone string) (string, e
 	return user.Email, nil
 }
 
+// func (s *sendCode) SendCode(ctx context.Context, phone string, vcode string) error {
+// 	signer := core.Signer{
+// 		Key:    "9a9a78319abd43348b43ec59d23b44bb",
+// 		Secret: "ed588c47c681417fabe13c612dcfb046",
+// 	}
+// 	templateId := "JM1000345"
+// 	url := fmt.Sprintf("https://smssend.apistore.huaweicloud.com/sms/send?receive=" + phone + "&templateId=" + templateId + "&values=" + vcode)
+// 	fmt.Printf("url: %+v\n", url)
+
+// 	r, _ := http.NewRequest("POST", url,
+// 		ioutil.NopCloser(bytes.NewBuffer([]byte("foo=bar"))))
+
+// 	r.Header.Add("x-stage", "RELEASE")
+// 	signer.Sign(r)
+
+// 	fmt.Printf("request: %+v\n", r)
+// 	resp, err := http.DefaultClient.Do(r)
+// 	fmt.Printf("resp: %+v\n", resp)
+// 	if err != nil {
+// 		return err
+// 	} else {
+// 		body, _ := ioutil.ReadAll(resp.Body)
+// 		fmt.Printf("resp: %+v\n", body)
+// 	}
+// 	return nil
+// }
+
 func (s *sendCode) SendCode(ctx context.Context, phone string, vcode string) error {
-	signer := core.Signer{
-		Key:    "9a9a78319abd43348b43ec59d23b44bb",
-		Secret: "ed588c47c681417fabe13c612dcfb046",
-	}
-	templateId := "JM1000345"
-	url := fmt.Sprintf("https://smssend.apistore.huaweicloud.com/sms/send?receive=" + phone + "&templateId=" + templateId + "&values=" + vcode)
-	fmt.Printf("url: %+v\n", url)
+	//必填,请参考"开发准备"获取如下数据,替换为实际值
+	apiAddress := "https://smsapi.cn-south-1.myhuaweicloud.com:443/sms/batchSendSms/v1" //APP接入地址(在控制台"应用管理"页面获取)+接口访问URI
+	appKey := "NETTvTJJie9ax03v9K5T4DFB9EV6"                                            //APP_Key
+	appSecret := "txi9kXIrxW0dVNMyAulrJf7XFNP7"                                         //APP_Secret
+	sender := "8823022707732"                                                           //国内短信签名通道号或国际/港澳台短信通道号
+	templateId := "7103cdd480d14d0aa8c68954a7dbeb6e"                                    //模板ID
+
+	//条件必填,国内短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称
+	//国际/港澳台短信不用关注该参数
+	signature := "样叽" //签名名称
 
-	r, _ := http.NewRequest("POST", url,
-		ioutil.NopCloser(bytes.NewBuffer([]byte("foo=bar"))))
+	//必填,全局号码格式(包含国家码),示例:+86151****6789,多个号码之间用英文逗号分隔
+	receiver := "+86" + phone //短信接收人号码
 
-	r.Header.Add("x-stage", "RELEASE")
-	signer.Sign(r)
+	//选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
+	statusCallBack := ""
 
-	fmt.Printf("request: %+v\n", r)
-	resp, err := http.DefaultClient.Do(r)
-	fmt.Printf("resp: %+v\n", resp)
+	/*
+	 * 选填,使用无变量模板时请赋空值 string templateParas = "";
+	 * 单变量模板示例:模板内容为"您的验证码是${1}"时,templateParas可填写为"[\"369751\"]"
+	 * 双变量模板示例:模板内容为"您有${1}件快递请到${2}领取"时,templateParas可填写为"[\"3\",\"人民公园正门\"]"
+	 * 模板中的每个变量都必须赋值,且取值不能为空
+	 * 查看更多模板和变量规范:产品介绍>模板和变量规范
+	 */
+	templateParas := "[\"" + vcode + "\"]" //模板变量,此处以单变量验证码短信为例,请客户自行生成6位验证码,并定义为字符串类型,以杜绝首位0丢失的问题(例如:002569变成了2569)。
+	// templateParas := "[\"12345678\"]" //模板变量,此处以单变量验证码短信为例,请客户自行生成6位验证码,并定义为字符串类型,以杜绝首位0丢失的问题(例如:002569变成了2569)。
+
+	body := buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature)
+	headers := make(map[string]string)
+	headers["Content-Type"] = "application/x-www-form-urlencoded"
+	headers["Authorization"] = AUTH_HEADER_VALUE
+	headers["X-WSSE"] = buildWsseHeader(appKey, appSecret)
+	resp, err := post(apiAddress, []byte(body), headers)
 	if err != nil {
 		return err
-	} else {
-		body, _ := ioutil.ReadAll(resp.Body)
-		fmt.Printf("resp: %+v\n", body)
 	}
+	fmt.Println(resp)
 	return nil
 }
+
+/**
+* sender,receiver,templateId不能为空
+ */
+func buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature string) string {
+	param := "from=" + url.QueryEscape(sender) + "&to=" + url.QueryEscape(receiver) + "&templateId=" + url.QueryEscape(templateId)
+	if templateParas != "" {
+		param += "&templateParas=" + url.QueryEscape(templateParas)
+	}
+	if statusCallBack != "" {
+		param += "&statusCallback=" + url.QueryEscape(statusCallBack)
+	}
+	if signature != "" {
+		param += "&signature=" + url.QueryEscape(signature)
+	}
+	return param
+}
+
+func post(url string, param []byte, headers map[string]string) (string, error) {
+	tr := &http.Transport{
+		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+	}
+	client := &http.Client{Transport: tr}
+
+	req, err := http.NewRequest("POST", url, bytes.NewBuffer(param))
+	if err != nil {
+		return "", err
+	}
+	for key, header := range headers {
+		req.Header.Set(key, header)
+	}
+
+	resp, err := client.Do(req)
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return "", err
+	}
+	return string(body), nil
+}
+
+func buildWsseHeader(appKey, appSecret string) string {
+	var cTime = time.Now().Format("2006-01-02T15:04:05Z")
+	var nonce = uuid.NewV4().String()
+	nonce = strings.ReplaceAll(nonce, "-", "")
+
+	h := sha256.New()
+	h.Write([]byte(nonce + cTime + appSecret))
+	passwordDigestBase64Str := base64.StdEncoding.EncodeToString(h.Sum(nil))
+
+	return fmt.Sprintf(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, cTime)
+}