From 917f1817e54df6483c2a7a12c8612f67eb62d40c Mon Sep 17 00:00:00 2001 From: Afeyer <1500527791@qq.com> Date: Thu, 19 Aug 2021 21:33:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=94=AF=E6=8C=81=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E5=AE=A2=E6=9C=8D=E5=9B=9E=E8=B0=83=E8=AF=B7=E6=B1=82=E7=9A=84?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E5=92=8C=E6=B6=88=E6=81=AF=E7=9A=84=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=EF=BC=8C=E5=A4=8D=E7=94=A8=E5=8E=9F=E6=9C=89=E7=9A=84?= =?UTF-8?q?Signature=E5=92=8CDecryptMsg=E6=96=B9=E6=B3=95=20(#439)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 添加微信客服SDK * polish:优化签名函数 * polish:优化注释内容 * polish:复用已有的Token以及CommonError,移除无用的输出 * polish:复用已有的消息加解密 * fix:修复错误信息被覆盖的问题 * polish:go fmt 文件 * polish:客服链接支持自定义参数并更新注释文档内容 * feat:支持微信客服回调请求的校验和消息的解析,复用原有的Signature和DecryptMsg方法 --- work/kf/callback.go | 96 +++++++++++++++++++++++++++++++++++++ work/kf/error.go | 8 ++++ work/kf/syncmsg/callback.go | 10 ---- 3 files changed, 104 insertions(+), 10 deletions(-) create mode 100644 work/kf/callback.go delete mode 100644 work/kf/syncmsg/callback.go diff --git a/work/kf/callback.go b/work/kf/callback.go new file mode 100644 index 0000000..a5b2863 --- /dev/null +++ b/work/kf/callback.go @@ -0,0 +1,96 @@ +package kf + +import ( + "encoding/xml" + + "github.com/silenceper/wechat/v2/util" +) + +// SignatureOptions 微信服务器验证参数 +type SignatureOptions struct { + Signature string `form:"msg_signature"` + TimeStamp string `form:"timestamp"` + Nonce string `form:"nonce"` + EchoStr string `form:"echostr"` +} + +// VerifyURL 验证请求参数是否合法并返回解密后的消息内容 +// //Gin框架的使用示例 +// r.GET("/v1/event/callback", func(c *gin.Context) { +// options := kf.SignatureOptions{} +// //获取回调的的校验参数 +// if = c.ShouldBindQuery(&options); err != nil { +// c.String(http.StatusUnauthorized, "参数解析失败") +// } +// // 调用VerifyURL方法校验当前请求,如果合法则把解密后的内容作为响应返回给微信服务器 +// echo, err := kfClient.VerifyURL(options) +// if err == nil { +// c.String(http.StatusOK, echo) +// } else { +// c.String(http.StatusUnauthorized, "非法请求来源") +// } +// }) +func (r *Client) VerifyURL(options SignatureOptions) (string, error) { + if options.Signature != util.Signature(r.ctx.Token, options.TimeStamp, options.Nonce, options.EchoStr) { + return "", NewSDKErr(40015) + } + _, bData, err := util.DecryptMsg(r.corpID, options.EchoStr, r.encodingAESKey) + if err != nil { + return "", NewSDKErr(40016) + } + + return string(bData), nil +} + +// 原始回调消息内容 +type callbackOriginMessage struct { + ToUserName string // 企业微信的CorpID,当为第三方套件回调事件时,CorpID的内容为suiteid + AgentID string // 接收的应用id,可在应用的设置页面获取 + Encrypt string // 消息结构体加密后的字符串 +} + +// CallbackMessage 微信客服回调消息 +type CallbackMessage struct { + ToUserName string `json:"to_user_name"` // 微信客服组件ID + CreateTime int `json:"create_time"` // 消息创建时间,unix时间戳 + MsgType string `json:"msgtype"` // 消息的类型,此时固定为 event + Event string `json:"event"` // 事件的类型,此时固定为 kf_msg_or_event + Token string `json:"token"` // 调用拉取消息接口时,需要传此token,用于校验请求的合法性 +} + +// GetCallbackMessage 获取回调事件中的消息内容 +// //Gin框架的使用示例 +// r.POST("/v1/event/callback", func(c *gin.Context) { +// var ( +// message kf.CallbackMessage +// body []byte +// ) +// // 读取原始消息内容 +// body, err = c.GetRawData() +// if err != nil { +// c.String(http.StatusInternalServerError, err.Error()) +// return +// } +// // 解析原始数据 +// message, err = kfClient.GetCallbackMessage(body) +// if err != nil { +// c.String(http.StatusInternalServerError, "消息获取失败") +// return +// } +// fmt.Println(message) +// c.String(200, "ok") +// }) +func (r *Client) GetCallbackMessage(encryptedMsg []byte) (msg CallbackMessage, err error) { + var origin callbackOriginMessage + if err = xml.Unmarshal(encryptedMsg, &origin); err != nil { + return msg, err + } + _, bData, err := util.DecryptMsg(r.corpID, origin.Encrypt, r.encodingAESKey) + if err != nil { + return msg, NewSDKErr(40016) + } + if err = xml.Unmarshal(bData, &msg); err != nil { + return msg, err + } + return msg, err +} diff --git a/work/kf/error.go b/work/kf/error.go index 7ddb9ba..20c9381 100644 --- a/work/kf/error.go +++ b/work/kf/error.go @@ -21,6 +21,10 @@ const ( SDKInvalidCorpID Error = "无效的 CorpID" // SDKAccessTokenInvalid 错误码:40014 SDKAccessTokenInvalid Error = "AccessToken 无效" + // SDKValidateSignatureFailed 错误码:40015 + SDKValidateSignatureFailed Error = "校验签名错误" + // SDKDecryptMSGFailed 错误码:40016 + SDKDecryptMSGFailed Error = "消息解密失败" // SDKAccessTokenMissing 错误码:41001 SDKAccessTokenMissing Error = "缺少AccessToken参数" // SDKAccessTokenExpired 错误码:42001 @@ -53,6 +57,10 @@ func NewSDKErr(code int64, msgList ...string) Error { return SDKInvalidCorpID case 40014: return SDKAccessTokenInvalid + case 40015: + return SDKValidateSignatureFailed + case 40016: + return SDKDecryptMSGFailed case 45009: return SDKApiFreqOutOfLimit case 95011: diff --git a/work/kf/syncmsg/callback.go b/work/kf/syncmsg/callback.go deleted file mode 100644 index 9558aa9..0000000 --- a/work/kf/syncmsg/callback.go +++ /dev/null @@ -1,10 +0,0 @@ -package syncmsg - -// Event 微信客服回调事件 -type Event struct { - ToUserName string `json:"to_user_name"` // 微信客服组件ID - CreateTime int `json:"create_time"` // 消息创建时间,unix时间戳 - MsgType string `json:"msgtype"` // 消息的类型,此时固定为 event - Event string `json:"event"` // 事件的类型,此时固定为 kf_msg_or_event - Token string `json:"token"` // 调用拉取消息接口时,需要传此token,用于校验请求的合法性 -}