Your Name 3 лет назад
Родитель
Сommit
7d34a8b5c8
5 измененных файлов: 329 добавлений и 94 удалений
  1. 77
    0
      api/event/types.go
  2. 1
    32
      authorization.go
  3. 6
    0
      config/authorizer.go
  4. 0
    62
      decode.go
  5. 245
    0
      event.go

+ 77
- 0
api/event/types.go Просмотреть файл

@@ -0,0 +1,77 @@
1
+package event
2
+
3
+import "encoding/xml"
4
+
5
+// InfoType 说明
6
+const (
7
+	// INFO_TYPE_COMPONENT_VERIFY_TICKET 验证票据
8
+	INFO_TYPE_COMPONENT_VERIFY_TICKET = "component_verify_ticket"
9
+
10
+	// INFO_TYPE_AUTHORIZED 授权成功
11
+	INFO_TYPE_AUTHORIZED = "authorized"
12
+
13
+	// INFO_TYPE_UPDATEAUTHORIZED 更新授权
14
+	INFO_TYPE_UPDATEAUTHORIZED = "updateauthorized"
15
+
16
+	// INFO_TYPE_UNAUTHORIZED 取消授权
17
+	INFO_TYPE_UNAUTHORIZED = "unauthorized"
18
+)
19
+
20
+// CDATA 解析 xml cdata 字段
21
+type CDATA struct {
22
+	Value string `xml:",cdata"`
23
+}
24
+
25
+// ReceivedEncryptMessage 待解密数据
26
+type ReceivedEncryptMessage struct {
27
+	XMLName    xml.Name `xml:"xml"`
28
+	ToUserName string   `xml:"ToUserName"`
29
+	Encrypt    string   `xml:"Encrypt"`
30
+}
31
+
32
+type SendEncryptMessage struct {
33
+	XMLName      xml.Name `xml:"xml"`
34
+	Encrypt      CDATA    `xml:"Encrypt"`
35
+	MsgSignature string   `xml:"MsgSignature"`
36
+	TimeStamp    int64    `xml:"TimeStamp"`
37
+	Nonce        string   `xml:"Nonce"`
38
+}
39
+
40
+// AuthEvent 事件
41
+type AuthEvent struct {
42
+	XMLName    xml.Name `xml:"xml"`
43
+	AppID      string   `xml:"AppId"`
44
+	CreateTime int64    `xml:"CreateTime"`
45
+	InfoType   string   `xml:"InfoType"`
46
+}
47
+
48
+// ComponentVerifyTicket 令牌授权事件
49
+type ComponentVerifyTicket struct {
50
+	AuthEvent
51
+	ComponentVerifyTicket string `xml:"ComponentVerifyTicket"`
52
+}
53
+
54
+// Authorized 授权成功, 与更新授权字段属性一致
55
+type Authorized struct {
56
+	AuthEvent
57
+	AuthorizerAppID              string `xml:"AuthorizerAppid"`
58
+	AuthorizationCode            string `xml:"AuthorizationCode"`
59
+	AuthorizationCodeExpiredTime int64  `xml:"AuthorizationCodeExpiredTime"`
60
+	PreAuthCode                  string `xml:"PreAuthCode"`
61
+}
62
+
63
+// Unauthorized 取消授权
64
+type Unauthorized struct {
65
+	AuthEvent
66
+	AuthorizerAppID string `xml:"AuthorizerAppid"`
67
+}
68
+
69
+// AuthorizerEvent 公众号或者小程序事件
70
+type AuthorizerEvent struct {
71
+	XMLName      xml.Name `xml:"xml"`
72
+	ToUserName   CDATA    `xml:"ToUserName"`
73
+	FromUserName CDATA    `xml:"FromUserName"`
74
+	CreateTime   int64    `xml:"CreateTime"`
75
+	MsgType      CDATA    `xml:"MsgType"`
76
+	Event        CDATA    `xml:"Event"`
77
+}

+ 1
- 32
authorization.go Просмотреть файл

@@ -20,12 +20,6 @@ import (
20 20
 
21 21
 // 授权与Token
22 22
 
23
-// VerifyTicket 验证票据
24
-func VerifyTicket(ticket string) error {
25
-	expire := utils.GetExpireTime(12 * 3600) // component_verify_ticket 的有效时间为12小时
26
-	return config.RefreshVerifyTicket(ticket, expire)
27
-}
28
-
29 23
 // RefreshToken 获取令牌
30 24
 // 主要是定时任务调用
31 25
 func RefreshToken() error {
@@ -56,33 +50,8 @@ func CreateAuthLink(client, url, appID string) (string, error) {
56 50
 	return authorization.GetAuthLink(client, preAuth.PreAuthCode, url, appID, 3)
57 51
 }
58 52
 
59
-// RefreshAuthorizerInfo 刷新授权对象信息
60
-func RefreshAuthorizerInfo(authCode string) error {
61
-	res, err := authorization.GetQueryAuth(authCode)
62
-	if err != nil {
63
-		return err
64
-	}
65
-
66
-	authInfo := res.AuthorizationInfo
67
-	appID := authInfo.AuthorizerAppID
68
-	expire := utils.GetExpireTime(authInfo.ExpiresIn)
69
-
70
-	funcLst := make([]int, 0)
71
-	if nil != authInfo.FuncInfo && len(authInfo.FuncInfo) > 0 {
72
-		for _, info := range authInfo.FuncInfo {
73
-			funcLst = append(funcLst, info.FuncscopeCategory.ID)
74
-		}
75
-	}
76
-
77
-	config.GetAuthorizer().RefreshFuncInfo(appID, funcLst)
78
-	if err := config.GetAuthorizer().RefreshToken(appID, authInfo.AuthorizerAccessToken, authInfo.AuthorizerRefreshToken, expire); err != nil {
79
-		return err
80
-	}
81
-
82
-	return nil
83
-}
84
-
85 53
 // RefreshAuthorizerToken 刷新接口调用令牌
54
+// 主要是定时任务调用
86 55
 func RefreshAuthorizerToken(appID string) error {
87 56
 	res, err := authorization.RefreshAuthorizerToken(appID, config.GetAuthorizer().GetRefreshToken(appID))
88 57
 	if err != nil {

+ 6
- 0
config/authorizer.go Просмотреть файл

@@ -29,6 +29,12 @@ type AuthorizerConfig interface {
29 29
 
30 30
 	// RefreshFuncInfo 刷新权限集列表
31 31
 	RefreshFuncInfo(appID string, lst []int)
32
+
33
+	// RefreshAuthStatus 刷新平台授权状态
34
+	RefreshAuthStatus(appID, status string)
35
+
36
+	// ProcessMessage 处理事件或者消息
37
+	ProcessMessage(msgType, event string, data []byte) error
32 38
 }
33 39
 
34 40
 var authorizer AuthorizerConfig

+ 0
- 62
decode.go Просмотреть файл

@@ -1,62 +0,0 @@
1
-/**
2
- * Copyright (c) 2022 Yansen Zhang
3
- * wxcomponent is licensed under Mulan PSL v2.
4
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
5
- * You may obtain a copy of Mulan PSL v2 at:
6
- *          http://license.coscl.org.cn/MulanPSL2
7
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
8
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
9
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
10
- * See the Mulan PSL v2 for more details.
11
-**/
12
-
13
-package wxcomponent
14
-
15
-import (
16
-	"encoding/base64"
17
-	"encoding/xml"
18
-
19
-	"gitee.com/yansen_zh/wxcomponent/utils/encrypt"
20
-	"gitee.com/yansen_zh/wxcomponent/utils/log"
21
-)
22
-
23
-// EncryptMessage 待解密数据
24
-type EncryptMessage struct {
25
-	XMLName    xml.Name `xml:"xml"`
26
-	ToUserName string   `xml:"ToUserName"`
27
-	Encrypt    string   `xml:"Encrypt"`
28
-}
29
-
30
-// DecodeMessage 解密消息
31
-func DecodeMessage(src []byte) (*encrypt.XMLMap, error) {
32
-	log.Info("解码 xml 数据: ", string(src))
33
-
34
-	msg := EncryptMessage{}
35
-	if err := xml.Unmarshal(src, &msg); err != nil {
36
-		log.Error("解码 xml 数据失败: ", err.Error())
37
-		return nil, err
38
-	}
39
-
40
-	bt1, e1 := base64.StdEncoding.DecodeString(msg.Encrypt)
41
-	if e1 != nil {
42
-		log.Error("解码 base 数据失败: ", e1.Error())
43
-		log.Error("原始 base64 数据: ", msg.Encrypt)
44
-		return nil, e1
45
-	}
46
-
47
-	bt2, e2 := encrypt.MsgDecode(bt1, aesKey)
48
-	if e2 != nil {
49
-		log.Error("解码加密数据失败: ", e2.Error())
50
-		log.Error("待解密数据: ", string(bt1))
51
-		return nil, e2
52
-	}
53
-
54
-	res := encrypt.XMLMap{}
55
-	if err := xml.Unmarshal(bt2, &res); err != nil {
56
-		log.Error("解码 xml 数据失败: ", err.Error())
57
-		log.Info("待解码 xml 数据: ", string(bt2))
58
-		return nil, err
59
-	}
60
-
61
-	return &res, nil
62
-}

+ 245
- 0
event.go Просмотреть файл

@@ -0,0 +1,245 @@
1
+/**
2
+ * Copyright (c) 2022 Yansen Zhang
3
+ * wxcomponent is licensed under Mulan PSL v2.
4
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
5
+ * You may obtain a copy of Mulan PSL v2 at:
6
+ *          http://license.coscl.org.cn/MulanPSL2
7
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
8
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
9
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
10
+ * See the Mulan PSL v2 for more details.
11
+**/
12
+
13
+package wxcomponent
14
+
15
+import (
16
+	"crypto/sha1"
17
+	"encoding/base64"
18
+	"encoding/binary"
19
+	"encoding/xml"
20
+	"fmt"
21
+	"sort"
22
+	"strconv"
23
+	"strings"
24
+	"time"
25
+
26
+	"gitee.com/yansen_zh/wxcomponent/api/authorization"
27
+	"gitee.com/yansen_zh/wxcomponent/api/event"
28
+	"gitee.com/yansen_zh/wxcomponent/config"
29
+	"gitee.com/yansen_zh/wxcomponent/utils"
30
+	"gitee.com/yansen_zh/wxcomponent/utils/encrypt"
31
+	"gitee.com/yansen_zh/wxcomponent/utils/log"
32
+)
33
+
34
+// DecodeMessage 解密消息
35
+func DecodeMessage(src []byte) ([]byte, error) {
36
+	log.Info("解码 xml 数据: ", string(src))
37
+
38
+	msg := event.ReceivedEncryptMessage{}
39
+	if err := xml.Unmarshal(src, &msg); err != nil {
40
+		log.Error("解码 xml 数据失败: ", err.Error())
41
+		return nil, err
42
+	}
43
+
44
+	bt1, e1 := base64.StdEncoding.DecodeString(msg.Encrypt)
45
+	if e1 != nil {
46
+		log.Error("解码 base 数据失败: ", e1.Error())
47
+		log.Error("原始 base64 数据: ", msg.Encrypt)
48
+		return nil, e1
49
+	}
50
+
51
+	bt2, e2 := encrypt.MsgDecode(bt1, aesKey)
52
+	if e2 != nil {
53
+		log.Error("解码加密数据失败: ", e2.Error())
54
+		log.Error("待解密数据: ", string(bt1))
55
+		return nil, e2
56
+	}
57
+
58
+	return bt2, nil
59
+}
60
+
61
+// toBigEndian 转换为网络大端序
62
+func toBigEndian(i int) []byte {
63
+	bt := make([]byte, 4)
64
+	binary.BigEndian.PutUint32(bt, uint32(i))
65
+	return bt
66
+}
67
+
68
+// EncodeMessage 加密消息
69
+func EncodeMessage(appID string, src []byte) ([]byte, error) {
70
+	log.Info("待加密数据APPID: ", appID)
71
+	log.Info("待加密数据: ", string(src))
72
+
73
+	rand16 := []byte(utils.RandStr(16))
74
+	msgLen := toBigEndian(len(src))
75
+
76
+	// 待加密数据 = random(16B)+ msg_len(4B) + msg + appid
77
+	data := append(rand16, msgLen...)
78
+	data = append(data, msgLen...)
79
+	data = append(data, src...)
80
+	data = append(data, []byte(appID)...)
81
+
82
+	bt1, e1 := encrypt.MsgEncode(data, aesKey)
83
+	if e1 != nil {
84
+		log.Error("加密数据失败: ", e1.Error())
85
+		log.Error("待加密数据: ", string(bt1))
86
+		return nil, e1
87
+	}
88
+
89
+	encryptStr := base64.StdEncoding.EncodeToString(bt1)
90
+	timestamp := time.Now().Unix()
91
+	timestampStr := strconv.FormatInt(timestamp, 10)
92
+	nonceStr := utils.RandStr(16)
93
+
94
+	strs := []string{
95
+		timestampStr,
96
+		nonceStr,
97
+		config.GetAccessToken(),
98
+		encryptStr,
99
+	}
100
+	sort.Strings(strs)
101
+	signature := fmt.Sprintf("%x", sha1.Sum([]byte(strings.Join(strs, ""))))
102
+
103
+	evt := event.SendEncryptMessage{
104
+		Encrypt:      event.CDATA{Value: encryptStr},
105
+		MsgSignature: signature,
106
+		TimeStamp:    timestamp,
107
+		Nonce:        nonceStr,
108
+	}
109
+
110
+	res, err := xml.Marshal(&evt)
111
+	if err != nil {
112
+		log.Error("转换 xml 数据失败: ", err.Error())
113
+		log.Error("待转换 xml 数据: ", evt)
114
+		return nil, e1
115
+	}
116
+
117
+	return res, nil
118
+}
119
+
120
+// DecodeAuthEvent 解密事件
121
+func DecodeAuthEvent(src []byte) (*event.AuthEvent, []byte, error) {
122
+	log.Info("待解密数据: ", string(src))
123
+
124
+	bt, e1 := DecodeMessage(src)
125
+	if e1 != nil {
126
+		return nil, nil, e1
127
+	}
128
+
129
+	evt := event.AuthEvent{}
130
+	if err := xml.Unmarshal(bt, evt); err != nil {
131
+		log.Error("解码 xml 数据失败: ", err.Error())
132
+		log.Error("待解码数据: ", string(bt))
133
+	}
134
+
135
+	// 如果是授权成功或者刷新授权, 则更新相关内容
136
+	if evt.InfoType == event.INFO_TYPE_AUTHORIZED || evt.InfoType == event.INFO_TYPE_UPDATEAUTHORIZED {
137
+		if err := handleAuthorized(bt); err != nil {
138
+			return nil, nil, err
139
+		}
140
+	}
141
+
142
+	// 如果是取消授权
143
+	if evt.InfoType == event.INFO_TYPE_UNAUTHORIZED {
144
+		handleUnauthorized(bt)
145
+	}
146
+
147
+	// 如果是刷新票据
148
+	if evt.InfoType == event.INFO_TYPE_COMPONENT_VERIFY_TICKET {
149
+		if err := handleComponentVerifyTicket(bt); err != nil {
150
+			return nil, nil, err
151
+		}
152
+	}
153
+
154
+	return &evt, bt, nil
155
+}
156
+
157
+// DecodeAuthorizerEvent 解密授权业务方的消息与事件
158
+func DecodeAuthorizerEvent(appID string, src []byte) (*event.AuthorizerEvent, []byte, error) {
159
+	log.Info("待解密数据APPID: ", appID)
160
+	log.Info("待解密数据: ", string(src))
161
+
162
+	bt, e1 := DecodeMessage(src)
163
+	if e1 != nil {
164
+		return nil, nil, e1
165
+	}
166
+
167
+	evt := event.AuthorizerEvent{}
168
+	if err := xml.Unmarshal(bt, evt); err != nil {
169
+		log.Error("解码 xml 数据失败: ", err.Error())
170
+		log.Error("待解码数据: ", string(bt))
171
+	}
172
+
173
+	authorizer := config.GetAuthorizer()
174
+	e2 := authorizer.ProcessMessage(evt.MsgType.Value, evt.Event.Value, bt)
175
+	if e2 != nil {
176
+		log.Error("处理事件或者消息失败: ", e2.Error())
177
+		log.Error("待处理消息: ", string(bt))
178
+	}
179
+
180
+	return &evt, bt, nil
181
+}
182
+
183
+// handleAuthorized 授权成功或刷新授权
184
+func handleAuthorized(data []byte) error {
185
+	authorizer := config.GetAuthorizer()
186
+	evtData := event.Authorized{}
187
+	xml.Unmarshal(data, &evtData)
188
+
189
+	// 获取授权信息
190
+	queryAuth, e1 := authorization.GetQueryAuth(evtData.AuthorizationCode)
191
+	if e1 != nil {
192
+		log.Error("获取授权信息失败: ", e1.Error())
193
+		return e1
194
+	}
195
+
196
+	appID := evtData.AuthorizerAppID
197
+	authInfo := queryAuth.AuthorizationInfo
198
+	expire := utils.GetExpireTime(authInfo.ExpiresIn)
199
+	funcLst := make([]int, 0)
200
+	if nil != authInfo.FuncInfo && len(authInfo.FuncInfo) > 0 {
201
+		for _, info := range authInfo.FuncInfo {
202
+			funcLst = append(funcLst, info.FuncscopeCategory.ID)
203
+		}
204
+	}
205
+
206
+	// 刷新平台授权状态
207
+	authorizer.RefreshAuthStatus(appID, evtData.InfoType)
208
+	// 刷新授权集
209
+	authorizer.RefreshFuncInfo(appID, funcLst)
210
+	// 刷新 token
211
+	e2 := authorizer.RefreshToken(appID, authInfo.AuthorizerAccessToken, authInfo.AuthorizerRefreshToken, expire)
212
+	if e2 != nil {
213
+		log.Error("刷新公众号或者小程序 Token 失败: ", e2.Error())
214
+		return e2
215
+	}
216
+
217
+	return nil
218
+}
219
+
220
+// handleUnauthorized 取消授权
221
+func handleUnauthorized(data []byte) error {
222
+	authorizer := config.GetAuthorizer()
223
+	evtData := event.Unauthorized{}
224
+	xml.Unmarshal(data, &evtData)
225
+	appID := evtData.AuthorizerAppID
226
+
227
+	// 刷新平台授权状态
228
+	authorizer.RefreshAuthStatus(appID, evtData.InfoType)
229
+	return nil
230
+}
231
+
232
+// handleComponentVerifyTicket 验证票据
233
+func handleComponentVerifyTicket(data []byte) error {
234
+	evtData := event.ComponentVerifyTicket{}
235
+	xml.Unmarshal(data, &evtData)
236
+
237
+	expire := utils.GetExpireTime(12 * 3600) // component_verify_ticket 的有效时间为12小时
238
+	err := config.RefreshVerifyTicket(evtData.ComponentVerifyTicket, expire)
239
+	if err != nil {
240
+		log.Error("刷新票据信息失败: ", err.Error())
241
+		return err
242
+	}
243
+
244
+	return nil
245
+}