首先說微信企業號的開發模式分為:編輯模式(普通模式)和開發模式(回調模式) ,在編輯模式下,只能做簡單的自定義菜單和自動回復消息,要想實現其他功能還得開啟開發者模式。
一、編輯模式和開發模式對消息的處理流程
1.編輯模式下,所有的業務流程都配置在微信服務器上,由它處理
2.開發模式,消息通過第三方服務器處理,最后經過微信服務器把消息發送給用戶
開發模式能處理的消息比編輯模式多,所以要先開啟開發模式才能開發更多功能。
二、開發模式的開啟
在回調模式下,企業不僅可以主動調用企業號接口,還可以接收用戶的消息或事件。接收的信息使用XML數據格式、UTF8編碼,并以AES方式加密。
1.開啟回調模式后要配置參數如下:
其中url是要訪問的servlet,token和EncodingAESKey是隨機獲取的,但要和項目中保持一致。
2.驗證URL的有效性
當你提交以上信息時,企業號將發送GET請求到填寫的URL上,GET請求攜帶四個參數,企業在獲取時需要做urldecode處理,否則會驗證不成功。
3.代碼
CoreServlet1類
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
public class CoreServlet1 extends HttpServlet { private static final long serialVersionUID = 4440739483644821986L; String sToken = "weixinCourse" ; String sCorpID = "wxe510946434680dab" ; String sEncodingAESKey = "DjlyZxgKiWRESIW2VnV9dSr7HsS7usWDfnwA8Q1ove1" ; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { WXBizMsgCrypt wxcpt; try { wxcpt = new WXBizMsgCrypt(sToken, sEncodingAESKey, sCorpID); String sVerifyMsgSig = request.getParameter( "msg_signature" ); String sVerifyTimeStamp = request.getParameter( "timestamp" ); String sVerifyNonce = request.getParameter( "nonce" ); String sVerifyEchoStr = request.getParameter( "echostr" ); String sEchoStr; sEchoStr = wxcpt.VerifyURL(sVerifyMsgSig, sVerifyTimeStamp, sVerifyNonce, sVerifyEchoStr); System.out.println( "verifyurl echostr: " + sEchoStr); PrintWriter out = response.getWriter(); out.print(sEchoStr); out.close(); out = null ; } catch (AesException e1) { e1.printStackTrace(); } } } |
工具類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
|
/** * 對公眾平臺發送給公眾賬號的消息加解密示例代碼. * * @copyright Copyright (c) 1998-2014 Tencent Inc. */ // ------------------------------------------------------------------------ /** * 針對org.apache.commons.codec.binary.Base64, * 需要導入架包commons-codec-1.9(或commons-codec-1.8等其他版本) * 官方下載地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi */ package com.qq.weixin.mp.aes; import java.nio.charset.Charset; import java.util.Arrays; import java.util.Random; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; /** * 提供接收和推送給公眾平臺消息的加解密接口(UTF8編碼的字符串). * <ol> * <li>第三方回復加密消息給公眾平臺</li> * <li>第三方收到公眾平臺發送的消息,驗證消息的安全性,并對消息進行解密。</li> * </ol> * 說明:異常java.security.InvalidKeyException:illegal Key Size的解決方案 * <ol> * <li>在官方網站下載JCE無限制權限策略文件(JDK7的下載地址: * http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html</li> * <li>下載后解壓,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li> * <li>如果安裝了JRE,將兩個jar文件放到%JRE_HOME%\lib\security目錄下覆蓋原來的文件</li> * <li>如果安裝了JDK,將兩個jar文件放到%JDK_HOME%\jre\lib\security目錄下覆蓋原來文件</li> * </ol> */ public class WXBizMsgCrypt { static Charset CHARSET = Charset.forName( "utf-8" ); Base64 base64 = new Base64(); byte [] aesKey; String token; String corpId; /** * 構造函數 * @param token 公眾平臺上,開發者設置的token * @param encodingAesKey 公眾平臺上,開發者設置的EncodingAESKey * @param corpId 企業的corpid * * @throws AesException 執行失敗,請查看該異常的錯誤碼和具體的錯誤信息 */ public WXBizMsgCrypt(String token, String encodingAesKey, String corpId) throws AesException { if (encodingAesKey.length() != 43 ) { throw new AesException(AesException.IllegalAesKey); } this .token = token; this .corpId = corpId; aesKey = Base64.decodeBase64(encodingAesKey + "=" ); } /** * 對密文進行解密. * * @param text 需要解密的密文 * @return 解密得到的明文 * @throws AesException aes解密失敗 */ String decrypt(String text) throws AesException { byte [] original; try { // 設置解密模式為AES的CBC模式 Cipher cipher = Cipher.getInstance( "AES/CBC/NoPadding" ); SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES" ); IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0 , 16 )); cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); // 使用BASE64對密文進行解碼 byte [] encrypted = Base64.decodeBase64(text); // 解密 original = cipher.doFinal(encrypted); } catch (Exception e) { e.printStackTrace(); throw new AesException(AesException.DecryptAESError); } String xmlContent, from_corpid; try { // 去除補位字符 byte [] bytes = PKCS7Encoder.decode(original); // 分離16位隨機字符串,網絡字節序和corpId byte [] networkOrder = Arrays.copyOfRange(bytes, 16 , 20 ); int xmlLength = recoverNetworkBytesOrder(networkOrder); xmlContent = new String(Arrays.copyOfRange(bytes, 20 , 20 + xmlLength), CHARSET); from_corpid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET); } catch (Exception e) { e.printStackTrace(); throw new AesException(AesException.IllegalBuffer); } // corpid不相同的情況 if (!from_corpid.equals(corpId)) { throw new AesException(AesException.ValidateCorpidError); } return xmlContent; } /** * 驗證URL * @param msgSignature 簽名串,對應URL參數的msg_signature * @param timeStamp 時間戳,對應URL參數的timestamp * @param nonce 隨機串,對應URL參數的nonce * @param echoStr 隨機串,對應URL參數的echostr * * @return 解密之后的echostr * @throws AesException 執行失敗,請查看該異常的錯誤碼和具體的錯誤信息 */ public String VerifyURL(String msgSignature, String timeStamp, String nonce, String echoStr) throws AesException { String signature = SHA1.getSHA1(token, timeStamp, nonce, echoStr); if (!signature.equals(msgSignature)) { throw new AesException(AesException.ValidateSignatureError); } String result = decrypt(echoStr); return result; } } /** * 對公眾平臺發送給公眾賬號的消息加解密示例代碼. * * @copyright Copyright (c) 1998-2014 Tencent Inc. */ // ------------------------------------------------------------------------ package com.qq.weixin.mp.aes; import java.security.MessageDigest; import java.util.Arrays; /** * SHA1 class * * 計算公眾平臺的消息簽名接口. */ class SHA1 { /** * 用SHA1算法生成安全簽名 * @param token 票據 * @param timestamp 時間戳 * @param nonce 隨機字符串 * @param encrypt 密文 * @return 安全簽名 * @throws AesException */ public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException { try { String[] array = new String[] { token, timestamp, nonce, encrypt }; StringBuffer sb = new StringBuffer(); // 字符串排序 Arrays.sort(array); for ( int i = 0 ; i < 4 ; i++) { sb.append(array[i]); } String str = sb.toString(); // SHA1簽名生成 MessageDigest md = MessageDigest.getInstance( "SHA-1" ); md.update(str.getBytes()); byte [] digest = md.digest(); StringBuffer hexstr = new StringBuffer(); String shaHex = "" ; for ( int i = 0 ; i < digest.length; i++) { shaHex = Integer.toHexString(digest[i] & 0xFF ); if (shaHex.length() < 2 ) { hexstr.append( 0 ); } hexstr.append(shaHex); } return hexstr.toString(); } catch (Exception e) { e.printStackTrace(); throw new AesException(AesException.ComputeSignatureError); } } } class PKCS7Encoder { static Charset CHARSET = Charset.forName( "utf-8" ); static int BLOCK_SIZE = 32 ; /** * 刪除解密后明文的補位字符 * * @param decrypted 解密后的明文 * @return 刪除補位字符后的明文 */ static byte [] decode( byte [] decrypted) { int pad = ( int ) decrypted[decrypted.length - 1 ]; if (pad < 1 || pad > 32 ) { pad = 0 ; } return Arrays.copyOfRange(decrypted, 0 , decrypted.length - pad); } } |
三、總結
企業通過參數msg_signature對請求進行校驗,如果確認此次GET請求來自企業號,那么企業應用對echostr參數解密并原樣返回echostr明文(不能加引號),則接入驗證生效,回調模式才能開啟。開啟后一些功能會陸續實現,敬請期待!
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。