123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501 |
- package wechat_service
-
- import (
- "bytes"
- "crypto/aes"
- "crypto/cipher"
- "crypto/rand"
- "crypto/sha1"
- "encoding/base64"
- "encoding/binary"
- "encoding/json"
- "encoding/xml"
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- math_rand "math/rand"
- "net/http"
- "sort"
- "strings"
- "time"
-
- "SCRM/service"
- "SCRM/utils"
-
- "github.com/astaxie/beego"
- "github.com/astaxie/beego/context"
- "github.com/astaxie/beego/httplib"
- "github.com/silenceper/wechat/util"
- )
-
- func init() {
- AesKey = EncodingAESKey2AESKey(encodingAESKey)
- }
-
- var (
- //以下均为公众号管理后台设置项
- token = beego.AppConfig.String("openwechattoken")
- appID = beego.AppConfig.String("openwechatappid")
- encodingAESKey = beego.AppConfig.String("openwechatencodingaeskey")
- )
-
- func MakeMsgSignature(timestamp, nonce, msg_encrypt string) string {
- sl := []string{token, timestamp, nonce, msg_encrypt}
- sort.Strings(sl)
- s := sha1.New()
- io.WriteString(s, strings.Join(sl, ""))
- return fmt.Sprintf("%x", s.Sum(nil))
- }
-
- func ValidateMsg(timestamp, nonce, msgEncrypt, msgSignatureIn string) bool {
- msgSignatureGen := MakeMsgSignature(timestamp, nonce, msgEncrypt)
- if msgSignatureGen != msgSignatureIn {
- return false
- }
- return true
- }
-
- func AesDecrypt(cipherData []byte, aesKey []byte) ([]byte, error) {
- k := len(aesKey) //PKCS#7
- if len(cipherData)%k != 0 {
- return nil, errors.New("crypto/cipher: ciphertext size is not multiple of aes key length")
- }
-
- block, err := aes.NewCipher(aesKey)
- if err != nil {
- return nil, err
- }
-
- iv := make([]byte, aes.BlockSize)
- if _, err := io.ReadFull(rand.Reader, iv); err != nil {
- return nil, err
- }
- blockMode := cipher.NewCBCDecrypter(block, iv)
- plainData := make([]byte, len(cipherData))
- blockMode.CryptBlocks(plainData, cipherData)
- return plainData, nil
- }
-
- var AesKey []byte
-
- func EncodingAESKey2AESKey(encodingKey string) []byte {
- data, _ := base64.StdEncoding.DecodeString(encodingKey + "=")
- return data
- }
- func ValidateAppId(id []byte) bool {
- if string(id) == appID {
- return true
- }
- return false
- }
-
- func ParseEncryptTextRequestBody(plainText []byte) (*TextRequestBody, error) {
-
- // Read length
- buf := bytes.NewBuffer(plainText[16:20])
- var length int32
- binary.Read(buf, binary.BigEndian, &length)
-
- // appID validation
- appIDstart := 20 + length
- id := plainText[appIDstart : int(appIDstart)+len(appID)]
- if !ValidateAppId(id) {
- return nil, errors.New("Appid is invalid")
- }
-
- textRequestBody := &TextRequestBody{}
- xml.Unmarshal(plainText[20:20+length], textRequestBody)
- return textRequestBody, nil
- }
-
- func Value2CDATA(v string) CDATAText {
- //return CDATAText{[]byte("<![CDATA[" + v + "]]>")}
- return CDATAText{"<![CDATA[" + v + "]]>"}
- }
- func MakeEncryptXmlData(fromUserName, toUserName, timestamp, content string) (string, error) {
- textResponseBody := &TextResponseBody{}
- textResponseBody.FromUserName = Value2CDATA(fromUserName)
- textResponseBody.ToUserName = Value2CDATA(toUserName)
- textResponseBody.MsgType = Value2CDATA("text")
- textResponseBody.Content = Value2CDATA(content)
- textResponseBody.CreateTime = timestamp
- body, err := xml.MarshalIndent(textResponseBody, " ", " ")
- if err != nil {
- return "", errors.New("xml marshal error")
- }
-
- buf := new(bytes.Buffer)
- err = binary.Write(buf, binary.BigEndian, int32(len(body)))
- if err != nil {
- return "", errors.New("Binary write err:" + err.Error())
- }
- bodyLength := buf.Bytes()
-
- randomBytes := []byte("abcdefghijklmnop")
-
- plainData := bytes.Join([][]byte{randomBytes, bodyLength, body, []byte(appID)}, nil)
- cipherData, err := AesEncrypt(plainData, AesKey)
- if err != nil {
- return "", errors.New("AesEncrypt error")
- }
- return base64.StdEncoding.EncodeToString(cipherData), nil
- }
-
- // PadLength calculates padding length, from github.com/vgorin/cryptogo
- func PadLength(slice_length, blocksize int) (padlen int) {
- padlen = blocksize - slice_length%blocksize
- if padlen == 0 {
- padlen = blocksize
- }
- return padlen
- }
-
- //from github.com/vgorin/cryptogo
- func PKCS7Pad(message []byte, blocksize int) (padded []byte) {
- // block size must be bigger or equal 2
- if blocksize < 1<<1 {
- panic("block size is too small (minimum is 2 bytes)")
- }
- // block size up to 255 requires 1 byte padding
- if blocksize < 1<<8 {
- // calculate padding length
- padlen := PadLength(len(message), blocksize)
-
- // define PKCS7 padding block
- padding := bytes.Repeat([]byte{byte(padlen)}, padlen)
-
- // apply padding
- padded = append(message, padding...)
- return padded
- }
- // block size bigger or equal 256 is not currently supported
- panic("unsupported block size")
- }
- func AesEncrypt(plainData []byte, aesKey []byte) ([]byte, error) {
- k := len(aesKey)
- if len(plainData)%k != 0 {
- plainData = PKCS7Pad(plainData, k)
- }
-
- block, err := aes.NewCipher(aesKey)
- if err != nil {
- return nil, err
- }
-
- iv := make([]byte, aes.BlockSize)
- if _, err := io.ReadFull(rand.Reader, iv); err != nil {
- return nil, err
- }
-
- cipherData := make([]byte, len(plainData))
- blockMode := cipher.NewCBCEncrypter(block, iv)
- blockMode.CryptBlocks(cipherData, plainData)
-
- return cipherData, nil
- }
- func MakeEncryptResponseBody(fromUserName, toUserName, content, nonce, timestamp string) ([]byte, error) {
- encryptBody := &EncryptResponseBody{}
-
- encryptXmlData, _ := MakeEncryptXmlData(fromUserName, toUserName, timestamp, content)
- encryptBody.Encrypt = Value2CDATA(encryptXmlData)
- encryptBody.MsgSignature = Value2CDATA(MakeMsgSignature(timestamp, nonce, encryptXmlData))
- encryptBody.TimeStamp = timestamp
- encryptBody.Nonce = Value2CDATA(nonce)
-
- return xml.MarshalIndent(encryptBody, " ", " ")
- }
-
- func SendMsgTypeTextMessage(appid string, ToUserName string, FromUserName string, text string, nonce string, timestamp string, Ctx *context.Context, orgID int64) {
-
- arthorizer, err := GetAuthorizationByAppID(orgID, appid)
- if err != nil {
- utils.ErrorLog("SendMsgTypeTextMessage error:%s", err)
- Ctx.WriteString("success")
- return
- }
-
- messages, err := GetTextReplyMessagesByKey(arthorizer.UserOrgId, text)
- if err != nil {
- utils.ErrorLog("SendMsgTypeTextMessage error:%s", err)
- Ctx.WriteString("success")
- return
- }
- if len(messages) == 0 {
- utils.ErrorLog("SendMsgTypeTextMessage error: messages is nil")
- Ctx.WriteString("success")
- return
- }
-
- r := math_rand.New(math_rand.NewSource(time.Now().UnixNano()))
- n := r.Intn(len(messages))
-
- for key, item := range messages {
- if key == n {
- //安全模式下向用户回复消息也需要加密
- respBody, e := MakeEncryptResponseBody(FromUserName, ToUserName, item.MessageContent, nonce, timestamp)
- if e != nil {
- Ctx.WriteString("success")
- return
- }
-
- Ctx.WriteString(string(respBody))
- fmt.Println(string(respBody))
- return
- }
-
- }
-
- Ctx.WriteString("success")
- return
-
- }
-
- //SendSubscribeTextMessage 当用户关注微信公众号,回复ta
- func SendSubscribeTextMessage(appid string, ToUserName string, FromUserName string, nonce string, timestamp string, Ctx *context.Context, orgID int64) {
- arthorizer, err := GetAuthorizationByAppID(orgID, appid)
- if err != nil {
- utils.ErrorLog("SendSubscribeTextMessage error:%s", err)
- Ctx.WriteString("success")
- return
- }
-
- message, err := GetOrgSubscribeReplyMessages(arthorizer.UserOrgId)
- if err != nil {
- utils.ErrorLog("SendSubscribeTextMessage error:%s", err)
- Ctx.WriteString("success")
- return
- }
- if message == nil {
- utils.ErrorLog("SendSubscribeTextMessage error: message is nil")
- Ctx.WriteString("success")
- return
- }
-
- respBody, e := MakeEncryptResponseBody(FromUserName, ToUserName, message.MessageContent, nonce, timestamp)
- if e != nil {
- Ctx.WriteString("success")
- return
- }
-
- Ctx.WriteString(string(respBody))
- return
-
- }
-
- func SendClickButtonMessage(appid string, ToUserName string, FromUserName string, keyName string, nonce string, timestamp string, Ctx *context.Context, orgID int64) {
- arthorizer, err := GetAuthorizationByAppID(orgID, appid)
- if err != nil {
- utils.ErrorLog("SendClickButtonMessage error:%s", err)
- Ctx.WriteString("success")
- return
- }
- message, err := GetClickButtonReplyMessagesByOrgID(arthorizer.UserOrgId, keyName)
- if err != nil {
- utils.ErrorLog("SendClickButtonMessage error:%s", err)
- Ctx.WriteString("success")
- return
- }
- if message == nil {
- utils.ErrorLog("SendClickButtonMessage error: message is nil")
- Ctx.WriteString("success")
- return
- }
-
- respBody, e := MakeEncryptResponseBody(FromUserName, ToUserName, message.MessageContent, nonce, timestamp)
- if e != nil {
- Ctx.WriteString("success")
- return
- }
-
- Ctx.WriteString(string(respBody))
- return
- }
-
- func GetReqPreAuthCode() (code string, err error) {
- redisClient := service.RedisClient()
- fmt.Println("redisClietent是设么", redisClient)
- defer redisClient.Close()
-
- componentAccessToken, err := redisClient.Get("sgj_patient:component_access_token").Result()
- fmt.Println("componentAccessToken是设么", componentAccessToken)
- if err != nil {
- utils.ErrorLog("component_access_token不存在")
- return
- }
-
- appID := beego.AppConfig.String("openwechatappid")
-
- type reqPreAuthCodeStruct struct {
- ComponentAppid string `json:"component_appid"`
- }
-
- // 通过 ComponentAccessToken 取 pre_auth_code
- // 3、获取预授权码pre_auth_code
- var ReqPreAuthCode reqPreAuthCodeStruct
- ReqPreAuthCode.ComponentAppid = appID
- //创建请求
-
- uri := fmt.Sprintf("%s?component_access_token=%s", "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode", componentAccessToken)
- var responseBytes []byte
- responseBytes, err = util.PostJSON(uri, ReqPreAuthCode)
- if err != nil {
- utils.ErrorLog("%s", err)
- return
- }
-
- var res MPResult
- err = json.Unmarshal(responseBytes, &res)
- if err != nil {
- utils.ErrorLog("%s", err)
- return
- }
-
- if res.ErrCode > 0 {
- utils.ErrorLog("%s", res.ErrMsg)
- return
- }
-
- var pre_auth_code_struct PreAuthCode
- err = json.Unmarshal(responseBytes, &pre_auth_code_struct)
- if err != nil {
- utils.ErrorLog("pre_auth_code_struct Unmarshal json error:%s", err)
- return
- }
- code = pre_auth_code_struct.PreAuthCode
- return
- }
-
- //ComponentAPIQueryAuth 使用授权码换取公众号的接口调用凭据和授权信息
- func ComponentAPIQueryAuth(AuthorizationCode string, ComponentAccessToken string) ([]byte, error) {
-
- //post的body内容,当前为json格式
- reqbody := "{\"component_appid\":\"" + beego.AppConfig.String("openwechatappid") + "\",\"authorization_code\": \"" + AuthorizationCode + "\"}"
- //创建请求
- postReq, err := http.NewRequest("POST",
- "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token="+ComponentAccessToken,
- strings.NewReader(reqbody)) //post内容
-
- if err != nil {
- utils.ErrorLog("POST请求:omponent/api_query_auth请求创建失败:%s", err)
- return nil, err
- }
-
- //增加header
- postReq.Header.Set("Content-Type", "application/json; encoding=utf-8")
-
- //执行请求
- client := &http.Client{}
- resp, err := client.Do(postReq)
- if err != nil {
- utils.ErrorLog("POST请求:创建请求失败:%s", err)
- return nil, err
- }
- //读取响应
- body, err := ioutil.ReadAll(resp.Body) //此处可增加输入过滤
- if err != nil {
- utils.ErrorLog("POST请求:读取body失败:%s", err)
- return nil, err
- }
-
- resp.Body.Close()
- return body, nil
-
- }
-
- //ComponentAPIGetAuthorizerInfo 利用AuthorizerAppid(公众号授权后,获取的appid)拉取公众号信息
- func ComponentAPIGetAuthorizerInfo(AuthorizerAppid string, ComponentAccessToken string) ([]byte, error) {
- //post的body内容,当前为json格式
- reqbody := "{\"component_appid\":\"" + beego.AppConfig.String("openwechatappid") + "\",\"authorizer_appid\": \"" + AuthorizerAppid + "\"}"
- //创建请求
- postReq, err := http.NewRequest("POST",
- "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token="+ComponentAccessToken, //post链接
- strings.NewReader(reqbody)) //post内容
-
- if err != nil {
- fmt.Println("POST请求:omponent/api_get_authorizer_info请求创建失败")
- return nil, err
- }
-
- //增加header
- postReq.Header.Set("Content-Type", "application/json; encoding=utf-8")
-
- //执行请求
- client := &http.Client{}
- resp, err := client.Do(postReq)
- if err != nil {
- fmt.Println("POST请求:创建请求失败")
- return nil, err
- }
- //读取响应
- body, err := ioutil.ReadAll(resp.Body) //此处可增加输入过滤
- if err != nil {
- fmt.Println("POST请求:读取body失败")
- return nil, err
- }
-
- resp.Body.Close()
- return body, nil
- }
-
- func PostJSON(uri string, jsonData []byte) ([]byte, error) {
- // jsonData, err := json.Marshal(obj)
- // if err != nil {
- // return nil, err
- // }
-
- jsonData = bytes.Replace(jsonData, []byte("\\u003c"), []byte("<"), -1)
- jsonData = bytes.Replace(jsonData, []byte("\\u003e"), []byte(">"), -1)
- jsonData = bytes.Replace(jsonData, []byte("\\u0026"), []byte("&"), -1)
-
- body := bytes.NewBuffer(jsonData)
- response, err := http.Post(uri, "application/json;charset=utf-8", body)
- if err != nil {
- return nil, err
- }
- defer response.Body.Close()
-
- if response.StatusCode != http.StatusOK {
- return nil, fmt.Errorf("http get error : uri=%v , statusCode=%v", uri, response.StatusCode)
- }
- return ioutil.ReadAll(response.Body)
- }
-
- func SendMpWechatMenus(AuthorizerAccessToken string, jsonData []byte) (err error) {
- //读取响应
- body, err := PostJSON("https://api.weixin.qq.com/cgi-bin/menu/create?access_token="+AuthorizerAccessToken, jsonData)
- fmt.Println("公众徐欧文是树森", err)
- fmt.Println("body", body)
- if err != nil {
- return
- }
- var result MPResult
- err = json.Unmarshal([]byte(body), &result)
- if err != nil {
- return
- }
- if result.ErrCode > 0 {
- utils.ErrorLog("MPResult ERROR:%v", result)
- err = errors.New(result.ErrMsg)
- return
- }
- return
-
- }
-
- func DeleteMpWechatMenus(AuthorizerAccessToken string) (err error) {
- uri := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=%s", AuthorizerAccessToken)
-
- b := httplib.Get(uri)
- var result MPResult
- err = b.ToJSON(&result)
- if err != nil {
- return
- }
- if result.ErrCode > 0 {
- err = errors.New(result.ErrMsg)
- return
- }
- return
- }
|