123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- /**
- * Copyright (c) 2022 Yansen Zhang
- * wxcomponent is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- * http://license.coscl.org.cn/MulanPSL2
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- **/
-
- package wxcomponent
-
- import (
- "crypto/sha1"
- "encoding/base64"
- "encoding/binary"
- "encoding/xml"
- "fmt"
- "sort"
- "strconv"
- "strings"
- "time"
-
- "gitee.com/yansen_zh/wxcomponent/api/authorization"
- "gitee.com/yansen_zh/wxcomponent/api/event"
- "gitee.com/yansen_zh/wxcomponent/config"
- "gitee.com/yansen_zh/wxcomponent/utils"
- "gitee.com/yansen_zh/wxcomponent/utils/encrypt"
- "gitee.com/yansen_zh/wxcomponent/utils/log"
- )
-
- // DecodeMessage 解密消息
- func DecodeMessage(src []byte) ([]byte, error) {
- log.Info("解码 xml 数据: ", string(src))
-
- msg := event.ReceivedEncryptMessage{}
- if err := xml.Unmarshal(src, &msg); err != nil {
- log.Error("解码 xml 数据失败: ", err.Error())
- return nil, err
- }
-
- bt1, e1 := base64.StdEncoding.DecodeString(msg.Encrypt)
- if e1 != nil {
- log.Error("解码 base 数据失败: ", e1.Error())
- log.Error("原始 base64 数据: ", msg.Encrypt)
- return nil, e1
- }
-
- bt2, e2 := encrypt.AESDecode(bt1, aesKey)
- if e2 != nil {
- log.Error("解码加密数据失败: ", e2.Error())
- log.Error("待解密数据: ", string(bt1))
- return nil, e2
- }
-
- return bt2, nil
- }
-
- // toBigEndian 转换为网络大端序
- func toBigEndian(i int) []byte {
- bt := make([]byte, 4)
- binary.BigEndian.PutUint32(bt, uint32(i))
- return bt
- }
-
- // EncodeMessage 加密消息
- func EncodeMessage(appID string, src []byte) ([]byte, error) {
- log.Info("待加密数据APPID: ", appID)
- log.Info("待加密数据: ", string(src))
-
- rand16 := []byte(utils.RandStr(16))
- msgLen := toBigEndian(len(src))
-
- // 待加密数据 = random(16B)+ msg_len(4B) + msg + appid
- data := append(rand16, msgLen...)
- data = append(data, msgLen...)
- data = append(data, src...)
- data = append(data, []byte(appID)...)
-
- bt1, e1 := encrypt.AESEncode(data, aesKey)
- if e1 != nil {
- log.Error("加密数据失败: ", e1.Error())
- log.Error("待加密数据: ", string(bt1))
- return nil, e1
- }
-
- encryptStr := base64.StdEncoding.EncodeToString(bt1)
- timestamp := time.Now().Unix()
- timestampStr := strconv.FormatInt(timestamp, 10)
- nonceStr := utils.RandStr(16)
-
- strs := []string{
- timestampStr,
- nonceStr,
- config.GetAccessToken(),
- encryptStr,
- }
- sort.Strings(strs)
- signature := fmt.Sprintf("%x", sha1.Sum([]byte(strings.Join(strs, ""))))
-
- evt := event.SendEncryptMessage{
- Encrypt: event.CDATA{Value: encryptStr},
- MsgSignature: signature,
- TimeStamp: timestamp,
- Nonce: nonceStr,
- }
-
- res, err := xml.Marshal(&evt)
- if err != nil {
- log.Error("转换 xml 数据失败: ", err.Error())
- log.Error("待转换 xml 数据: ", evt)
- return nil, e1
- }
-
- return res, nil
- }
-
- // DecodeAuthEvent 解密事件
- func DecodeAuthEvent(src []byte) (*event.AuthEvent, []byte, error) {
- log.Info("待解密数据: ", string(src))
-
- bt, e1 := DecodeMessage(src)
- if e1 != nil {
- return nil, nil, e1
- }
-
- evt := event.AuthEvent{}
- if err := xml.Unmarshal(bt, evt); err != nil {
- log.Error("解码 xml 数据失败: ", err.Error())
- log.Error("待解码数据: ", string(bt))
- }
-
- // 如果是授权成功或者刷新授权, 则更新相关内容
- if evt.InfoType == event.INFO_TYPE_AUTHORIZED || evt.InfoType == event.INFO_TYPE_UPDATEAUTHORIZED {
- if err := handleAuthorized(bt); err != nil {
- return nil, nil, err
- }
- }
-
- // 如果是取消授权
- if evt.InfoType == event.INFO_TYPE_UNAUTHORIZED {
- handleUnauthorized(bt)
- }
-
- // 如果是刷新票据
- if evt.InfoType == event.INFO_TYPE_COMPONENT_VERIFY_TICKET {
- if err := handleComponentVerifyTicket(bt); err != nil {
- return nil, nil, err
- }
- }
-
- return &evt, bt, nil
- }
-
- // DecodeAuthorizerEvent 解密授权业务方的消息与事件
- func DecodeAuthorizerEvent(appID string, src []byte) (*event.AuthorizerEvent, []byte, error) {
- log.Info("待解密数据APPID: ", appID)
- log.Info("待解密数据: ", string(src))
-
- bt, e1 := DecodeMessage(src)
- if e1 != nil {
- return nil, nil, e1
- }
-
- evt := event.AuthorizerEvent{}
- if err := xml.Unmarshal(bt, evt); err != nil {
- log.Error("解码 xml 数据失败: ", err.Error())
- log.Error("待解码数据: ", string(bt))
- }
-
- authorizer := config.GetAuthorizer()
- e2 := authorizer.ProcessMessage(evt.MsgType.Value, evt.Event.Value, bt)
- if e2 != nil {
- log.Error("处理事件或者消息失败: ", e2.Error())
- log.Error("待处理消息: ", string(bt))
- }
-
- return &evt, bt, nil
- }
-
- // handleAuthorized 授权成功或刷新授权
- func handleAuthorized(data []byte) error {
- authorizer := config.GetAuthorizer()
- evtData := event.Authorized{}
- xml.Unmarshal(data, &evtData)
-
- // 获取授权信息
- queryAuth, e1 := authorization.GetQueryAuth(evtData.AuthorizationCode)
- if e1 != nil {
- log.Error("获取授权信息失败: ", e1.Error())
- return e1
- }
-
- appID := evtData.AuthorizerAppID
- authInfo := queryAuth.AuthorizationInfo
- expire := utils.GetExpireTime(authInfo.ExpiresIn)
- funcLst := make([]int, 0)
- if nil != authInfo.FuncInfo && len(authInfo.FuncInfo) > 0 {
- for _, info := range authInfo.FuncInfo {
- funcLst = append(funcLst, info.FuncscopeCategory.ID)
- }
- }
-
- // 刷新平台授权状态
- authorizer.RefreshAuthStatus(appID, evtData.InfoType)
- // 刷新授权集
- authorizer.RefreshFuncInfo(appID, funcLst)
- // 刷新 token
- e2 := authorizer.RefreshToken(appID, authInfo.AuthorizerAccessToken, authInfo.AuthorizerRefreshToken, expire)
- if e2 != nil {
- log.Error("刷新公众号或者小程序 Token 失败: ", e2.Error())
- return e2
- }
-
- return nil
- }
-
- // handleUnauthorized 取消授权
- func handleUnauthorized(data []byte) error {
- authorizer := config.GetAuthorizer()
- evtData := event.Unauthorized{}
- xml.Unmarshal(data, &evtData)
- appID := evtData.AuthorizerAppID
-
- // 刷新平台授权状态
- authorizer.RefreshAuthStatus(appID, evtData.InfoType)
- return nil
- }
-
- // handleComponentVerifyTicket 验证票据
- func handleComponentVerifyTicket(data []byte) error {
- evtData := event.ComponentVerifyTicket{}
- xml.Unmarshal(data, &evtData)
-
- expire := utils.GetExpireTime(12 * 3600) // component_verify_ticket 的有效时间为12小时
- err := config.RefreshVerifyTicket(evtData.ComponentVerifyTicket, expire)
- if err != nil {
- log.Error("刷新票据信息失败: ", err.Error())
- return err
- }
-
- return nil
- }
|