In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-02 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article introduces to you what is the implementation step of java docking WeCom, the content is very detailed, interested friends can refer to, hope to be helpful to you.
Preface
Recently realized the community docking WeCom, the docking process encountered some points, recorded here.
Introduction of WeCom
WeCom has the same experience as Wechat, which is used for the management of internal members and external customers, from which the community ecology can be built.
WeCom provides a wealth of api to call for data management, as well as a variety of callback events, which can be known in time when the data changes.
We are divided into two parts to explain. The first part calls WeCom api, and the second part receives WeCom's callback.
Call WeCom api
Development document address for api: https://work.weixin.qq.com/api/doc/90000/90135/90664
What is necessary to call WeCom is the accesstoken of the enterprise. To get accesstoken, we need our corpid and corpsercret.
We can refer to https://work.weixin.qq.com/api/doc/90000/90135/91039 here for details.
With token, we can call various api through http requests to get data. As an example, create the api of the member, as follows, we can simply invoke it using the http tool.
A http calling tool is shared here.
@ Slf4jpublic class HttpUtils {static CloseableHttpClient httpClient; private HttpUtils () {throw new IllegalStateException ("Utility class");} static {Registry registry = RegistryBuilder.create () .register ("http", PlainConnectionSocketFactory.getSocketFactory ()) .register ("https", SSLConnectionSocketFactory.getSocketFactory ()) .build (); PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager (registry); connectionManager.setMaxTotal ConnectionManager.setDefaultMaxPerRoute; connectionManager.setDefaultSocketConfig (SocketConfig.custom (). SetSoTimeout (15, TimeUnit.SECONDS). SetTcpNoDelay (true). Build (); connectionManager.setValidateAfterInactivity (TimeValue.ofSeconds (15)) HttpClient = HttpClients.custom () .setConnectionManager (connectionManager) .String url AutomaticRetries () .build ();} public static String get (String url, Map paramMap, Map headerMap) {String param = paramMap.entrySet (). Stream (). Map (n-> n.getKey () + "=" + n.getValue ()) .automation (Collectors.joining ("&")) String fullUrl = url + "?" + param; final HttpGet httpGet = new HttpGet (fullUrl); if (Objects.nonNull (headerMap) & & headerMap.size () > 0) {headerMap.forEach ((key, value)-> httpGet.addHeader (key, value));} CloseableHttpResponse response = null; try {response = httpClient.execute (httpGet) String strResult = EntityUtils.toString (response.getEntity ()); if (200! = response.getCode ()) {log.error ("HTTP get return status is not 200 [resp= {}]", strResult);} return strResult;} catch (IOException | ParseException e) {log.error ("HTTP get exception", e); return "" } finally {if (null! = response) {try {EntityUtils.consume (response.getEntity ());} catch (IOException e) {e.printStackTrace () }} public static String post (String url,Map paramMap, Map headerMap, String data) {CloseableHttpResponse response = null; try {String param = paramMap.entrySet (). Stream (). Map (n-> n.getKey () + "=" + n.getValue ()) .map (Collectors.joining ("&")); String fullUrl = url + "?" + param Final HttpPost httpPost = new HttpPost (fullUrl); if (Objects.nonNull (headerMap) & & headerMap.size () > 0) {headerMap.forEach ((key, value)-> httpPost.addHeader (key, value));} StringEntity httpEntity = new StringEntity (data, StandardCharsets.UTF_8); httpPost.setEntity (httpEntity); response = httpClient.execute (httpPost) If (200 = = response.getCode ()) {String strResult = EntityUtils.toString (response.getEntity ()); return strResult;}} catch (IOException | ParseException e) {e.printStackTrace (); return "" } finally {if (null! = response) {try {EntityUtils.consume (response.getEntity ());} catch (IOException e) {e.printStackTrace ();} return ";}} docking WeCom callback
There are many kinds of callbacks. For example, the callback of the address book is as follows:
Https://work.weixin.qq.com/api/doc/90000/90135/90967
The overall callback process is as follows:
To configure the callback service, you need to have three configuration items, namely, URL, Token and EncodingAESKey.
First of all, URL is the callback service address, which is built by the developer to receive notification messages or events.
Second, Token is used to calculate signatures, custom strings that consist of English or numeric characters and are no more than 32 bits long. The URL provided by the developer is publicly accessible, which means that when you get the URL, you can push messages to the link. Then URL services need to solve two problems:
How to tell whether it is from WeCom or not?
How to tell whether the content of the push message has been tampered with
The above problems can be solved by digital signature. Specifically: Token is agreed as the key, which is only known by developers and WeCom. It is not visible in the transmission and is used to participate in signature calculation. When pushing the message, WeCom calculates the signature with the message content and Token. When the developer receives the push message, the signature is calculated according to the same algorithm. If it is the same signature, the trusted source is WeCom, and the content is complete.
If the source is not WeCom, the attacker cannot calculate the correct signature because the attacker does not have the correct Token.
If the message content is tampered with, the request will be rejected because the developer will recalculate the signature of the received message with the Token, and the value is not consistent with the signature of the parameter.
Finally, EncodingAESKey is used for message content encryption, a custom string consisting of English or numeric characters and a length of 43 bits. Because the message is transmitted on the open Internet, the message content can be intercepted, and if the content is not encrypted, the interceptor can read the message content directly. If the message contains some sensitive information, it is very dangerous. On the basis of this background, EncodingAESKey proposes to encrypt the content sent and assemble it into a certain format before sending it.
Docking callback, we need to achieve the above encryption, tampering and other code. The implementation of the java version is shared here.
AesException
Public class AesException extends Exception {public final static int OK = 0; public final static int ValidateSignatureError =-40001; public final static int ParseXmlError =-40002; public final static int ComputeSignatureError =-40003; public final static int IllegalAesKey =-40004; public final static int ValidateCorpidError =-40005; public final static int EncryptAESError =-40006; public final static int DecryptAESError =-40007; public final static int IllegalBuffer =-40008; private int code Private static String getMessage (int code) {switch (code) {case ValidateSignatureError: return "signature verification error"; case ParseXmlError: return "xml parsing failed" Case ComputeSignatureError: return "sha encryption failed to generate signature"; case IllegalAesKey: return "illegal SymmetricKey"; case ValidateCorpidError: return "corpid verification failed" Case EncryptAESError: return "aes encryption failed"; case DecryptAESError: return "aes decryption failure"; case IllegalBuffer: return "illegal buffer obtained after decryption" Default: return null;}} public int getCode () {return code;} AesException (int code) {super (getMessage (code)); this.code = code;}}
MessageUtil
Public class MessageUtil {/ * parses the request (XML) sent by Wechat. * * @ param msg message * @ return map * / public static Map parseXml (final String msg) {/ / store the parsing result in HashMap Map map = new HashMap () / / get input stream try from request (InputStream inputStream = new ByteArrayInputStream (msg.getBytes (StandardCharsets.UTF_8.name () {/ / read input stream SAXReader reader = new SAXReader (); Document document = reader.read (inputStream); / / get xml root element Element root = document.getRootElement () / / get all child nodes of the root element List elementList = root.elements (); / / traverse all child nodes for (Element e: elementList) {map.put (e.getName (), e.getText ());}} catch (Exception e) {e.printStackTrace () } return map;}} public enum QywechatEnum {TEST ("Test", "123123123123", "123123123123", "12312312312"); / * Application name * / private String name; / * * Enterprise ID * / private String corpid; / * * callback token * / private String token configured by url / * * Random encrypted string * / private String encodingAESKey; QywechatEnum (final String name, final String corpid, final String token, final String encodingAESKey) {this.name = name; this.corpid = encodingAESKey; this.token = token;} public String getCorpid () {return corpid;} public String getName () {return name } public String getToken () {return token;} public String getEncodingAESKey () {return encodingAESKey;}} public class QywechatInfo {/ * * signature * / private String msgSignature; / * * Random timestamp * / private String timest / * Random value * / private String nonce / * encrypted xml string * / private String sPostData; / * * WeCom callback configuration * / private QywechatEnum qywechatEnum } public class SHA1Utils {/ * generate security signature using SHA1 algorithm * * @ param token ticket * @ param timestamp timestamp * @ param nonce random string * @ param encrypt ciphertext * @ return security signature * @ 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 (); / / string sort 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); } }}public class WXBizMsgCrypt { static Charset CHARSET = Charset.forName("utf-8"); Base64 base64 = new Base64(); byte[] aesKey; String token; String receiveid; /** * 构造函数 * * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 */ public WXBizMsgCrypt(final QywechatEnum qywechatEnum) throws AesException { this.token = qywechatEnum.getToken(); this.receiveid = qywechatEnum.getCorpid(); String encodingAesKey = qywechatEnum.getEncodingAESKey(); if (encodingAesKey.length() != 43) { throw new AesException(AesException.IllegalAesKey); } aesKey = Base64.decodeBase64(encodingAesKey + "="); } // 生成4个字节的网络字节序 byte[] getNetworkBytesOrder(int sourceNumber) { byte[] orderBytes = new byte[4]; orderBytes[3] = (byte) (sourceNumber & 0xFF); orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF); orderBytes [1] = (byte) (sourceNumber > > 16 & 0xFF); orderBytes [0] = (byte) (sourceNumber > > 24 & 0xFF); return orderBytes;} / restore 4-byte network byte order int recoverNetworkBytesOrder (byte [] orderBytes) {int sourceNumber = 0; for (int I = 0; I < 4; iByte +) {sourceNumber
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.