I was busy for a while ago. I started to develop the WeChat official account and read the documents from scratch. I stepped on a lot of pitfalls. I have been through it. I have recently considered making some summary so that I can review it when I develop it in the future. I will also give a reference to students who are working on related projects.
1. Ideas
WeChat access: User messages and event pushes required by developers will initiate a request through the WeChat server and forward to the server URL address you configured on the public platform. The WeChat server will bring the four parameters of signature, timestamp, nonce, and echostr. Our own server splices the token configured by the public platform, as well as the timestamp uploaded, and nonce is encrypted in SHA1 and match the signature. Return the ture to indicate that the access is successful.
Message reply: When a user sends a message to the official account, the WeChat server will request the user message to the corresponding interface of the server we configured in xml format through POST. What we have to do is to perform corresponding logical processing based on the message type, etc., and return the final return result to the WeChat server through xml format, and the WeChat party will convey it to the user.
1. Public platform configuration
2.Controller
@Controller@RequestMapping("/wechat")publicclass WechatController { @Value("${DNBX_TOKEN}") private String DNBX_TOKEN; private static final Logger LOGGER = LoggerFactory.getLogger(WechatController.class); @Resource WechatService wechatService; /** * WeChat access* @param wc * @return * @throws IOException */ @RequestMapping(value="/connect",method = {RequestMethod.GET, RequestMethod.POST}) @ResponseBody publicvoid connectWeixin(HttpServletRequest request, HttpServletResponse response) throws IOException{ // Set the encoding of the request and response to UTF-8 (prevent Chinese garbled) request.setCharacterEncoding("UTF-8"); // The WeChat server uses UTF-8 encoding when POST messages, and the same encoding must be used when receiving, otherwise the Chinese will be garbled; response.setCharacterEncoding("UTF-8"); // When responding to messages (replying to the user), the encoding method is also set to UTF-8, the principle is the same as above; boolean isGet = request.getMethod().toLowerCase().equals("get"); PrintWriter out = response.getWriter(); try { if (isGet) { String signature = request.getParameter("signature");// WeChat encryption signature String timestamp = request.getParameter("timestamp");// Timestamp String nonce = request.getParameter("nonce");// Random number String echostr = request.getParameter("echostr");//Random string//Check the request by checking the signature. If the verification is successful, return echostr as is, indicating that the access is successful. Otherwise, the access fails if (SignUtil.checkSignature(DNBX_TOKEN, signature, timestamp, nonce)) { LOGGER.info("Connect the weixin server is successful."); response.getWriter().write(echostr); } else { LOGGER.error("Failed to verify the signature!"); } }else{ String respMessage = "Exception message! "; try { respMessage = wechatService.weixinPost(request); out.write(respMessage); LOGGER.info("The request completed successfully"); LOGGER.info("to weixin server "+respMessage); } catch (Exception e) { LOGGER.error("Failed to convert the message from weixin!"); } } } catch (Exception e) { LOGGER.error("Connect the weixin server is error."); } finally{ out.close(); } }}3. Signature verification checkSignature
From the controller above, we can see that I encapsulate a tool class SignUtil, called a checkSignature, and passed in four values, DNBX_TOKEN, signature, timestamp, nonce. This process is very important. In fact, we can understand it as an encryption and decryption process of the value transmitted by WeChat. In order to ensure security, all interfaces in many large projects will have such a verification process. DNBX_TOKEN We have configured a token string on the WeChat public platform, keep the idea confidential! The other three are parameters sent by the WeChat server to send a get request. We perform a layer of sha1 encryption:
public class SignUtil { /** * Verification signature * @param token WeChat server token, configured in the env.properties file and configured in the developer center must be consistent* @param signature The WeChat server sends the sha1 encrypted certificate signature* @param timestamp timestamp* @param nonce Random number* @return */ public static boolean checkSignature(String token,String signature, String timestamp, String nonce) { String[] arr = new String[] { token, timestamp, nonce }; // Sort dictionary order of token, timestamp, and nonce; // Splice the three parameter strings into a string for sha1 encryption String tmpStr = SHA1.encode(arr[0] + arr[1] + arr[2]); // The string encrypted sha1 can be compared with signature, identifying that the request comes from WeChat return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false; } } SHA1:
/** * WeChat Public Platform (JAVA) SDK * * SHA1 Algorithm * @author helijun 2016/06/15 19:49 */ public final class SHA1 { private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /** * Takes the raw bytes from the digest and formats them correct. * * @param bytes the raw bytes from the digest. * @return the formatted bytes. */ private static String getFormattedText(byte[] bytes) { int len = bytes.length; StringBuilder buf = new StringBuilder(len * 2); // Convert the ciphertext into a hexadecimal string form for (int j = 0; j < len; j++) { buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]); buf.append(HEX_DIGITS[bytes[j] & 0x0f]); } return buf.toString(); } public static String encode(String str) { if (str == null) { return null; } try { MessageDigest messageDigest = MessageDigest.getInstance("SHA1"); messageDigest.update(str.getBytes()); return getFormattedText(messageDigest.digest()); } catch (Exception e) { throw new RuntimeException(e); } } }When you submit and save it on the public platform and see the green prompt "Access Success", congratulations on completing WeChat access. This process requires a little more carefulness and pay attention to the case in the encryption algorithm. If the access is not successful, most of the cases are problems with the encryption algorithm, check it more.
4. Implement automatic message reply service
/** * Process requests from WeChat* * @param request * @return */ public String weixinPost(HttpServletRequest request) { String respMessage = null; try { // xml request parsing Map<String, String> requestMap = MessageUtil.xmlToMap(request); // Sender account (open_id) String fromUserName = requestMap.get("FromUserName"); // Public account String toUserName = requestMap.get("ToUserName"); // Message type String msgType = requestMap.get("MsgType"); // Message content String content = requestMap.get("Content"); LOGGER.info("FromUserName is:" + fromUserName + ", ToUserName is:" + toUserName + ", MsgType is:" + msgType); // Text message if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) { // Here you execute the corresponding logic according to the keywords, there is only one you can't think of, and there is no such thing as you can't do if(content.equals("xxx")) { } //Automatic reply TextMessage text = new TextMessage(); text.setContent("the text is" + content); text.setToUserName(fromUserName); text.setFromUserName(toUserName); text.setCreateTime(new Date().getTime() + ""); text.setMsgType(msgType); respMessage = MessageUtil.textMessageToXml(text); } /*else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {// Event push String eventType = requestMap.get("Event");// Event type if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {// Subscribe to respContent = "Welcome to follow the xxx official account! "; return MessageResponse.getTextMessage(fromUserName , toUserName , respContent); } else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {// Custom menu click event String eventKey = requestMap.get("EventKey");// Event KEY value, corresponding to the KEY value specified when creating the custom menu logger.info("eventKey is:" +eventKey); return xxx; } } // Turn on WeChat sound recognition test 2015-3-30 else if(msgType.equals("voice")) { String recvMessage = requestMap.get("Recognition"); //respContent = "Received speech analysis result: "+recvMessage; if(recvMessage!=null){ respContent = TulingApiProcess.getTulingResult(recvMessage); }else{ respContent = "What you said is too vague, can you say it again? "; } return MessageResponse.getTextMessage(fromUserName , toUserName , respContent); } //Photography function else if(msgType.equals("pic_sysphoto")) { } else { return MessageResponse.getTextMessage(fromUserName , toUserName , "Return to empty"); }*/ // Event push else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) { String eventType = requestMap.get("Event");// Event type// Subscribe if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) { TextMessage text = new TextMessage(); text.setContent("Welcome to follow, xxx"); text.setToUserName(fromUserName); text.setFromUserName(toUserName); text.setCreateTime(new Date().getTime() + ""); text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT); respMessage = MessageUtil.textMessageToXml(text); } // TODO After unsubscribe, the user cannot receive the message sent by the official account, so there is no need to reply to the message else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {// Unsubscribe} // Custom menu click event else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) { String eventKey = requestMap.get("EventKey");// Event KEY value corresponds to the KEY value specified when creating a custom menu if (eventKey.equals("customer_telephone")) { TextMessage text = new TextMessage(); text.setContent("0755-86671980"); text.setToUserName(fromUserName); text.setFromUserName(toUserName); text.setCreateTime(new Date().getTime() + ""); text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT); respMessage = MessageUtil.textMessageToXml(text); } } } } catch (Exception e) { Logger.error("error...") } return respMessage; }The code is posted as above. Most of them have comments. If you read the basic semantics once, you don’t need to explain them.
There is one place that needs special attention:
The red colored fromUserName and toUserName are exactly the opposite, which is also one of the pitfalls. I remember that I had adjusted it for a long time, but it was not a problem, but it was not working. Finally, I received the message after changing these two! In fact, it is correct to think about it. When you return to the WeChat server, your role will change, so the sender and receiver are definitely the opposite.
5.MessageUtil
public class MessageUtil { /** * Return message type: text*/ public static final String RESP_MESSAGE_TYPE_TEXT = "text"; /** * Return message type: Music*/ public static final String RESP_MESSAGE_TYPE_MUSIC = "music"; /** * Return message type: graphic text*/ public static final String RESP_MESSAGE_TYPE_NEWS = "news"; /** * Request message type: text*/ public static final String REQ_MESSAGE_TYPE_TEXT = "text"; /** * Request message type: Image*/ public static final String REQ_MESSAGE_TYPE_IMAGE = "image"; /** * Request message type: Link*/ public static final String REQ_MESSAGE_TYPE_LINK = "link"; /** * Request message type: Geographic location*/ public static final String REQ_MESSAGE_TYPE_LOCATION = "location"; /** * Request message type: Audio*/ public static final String REQ_MESSAGE_TYPE_VOICE = "voice"; /** * Request message type: push */ public static final String REQ_MESSAGE_TYPE_EVENT = "event"; /** * Event type: subscribe (subscribe) */ public static final String EVENT_TYPE_SUBSCRIBE = "subscribe"; /** * Event type: unsubscribe (unsubscribe) */ public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe"; /** * Event type: CLICK (custom menu click event) */ public static final String EVENT_TYPE_CLICK = "CLICK"; }Here, in order to make the program readability and scalability better, I have made some encapsulation, defined several constants, and encapsulated some parameters passed from WeChat into Java bean persistent objects. The core code is as above. Focus on the conversion between xml and map
In fact, this problem is attributed to WeChat using XML communication, and we usually use JSON, so we may feel a little uncomfortable in a short period of time.
1.Introduce jar package
<!-- parse xml --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.9</version> </dependency>
2.xml to map collection object
/** * xml to map * @param request * @return * @throws IOException */ @SuppressWarnings("unchecked") public static Map<String, String> xmlToMap(HttpServletRequest request) throws IOException{ Map<String, String> map = new HashMap<String, String>(); SAXReader reader = new SAXReader(); InputStream ins = null; try { ins = request.getInputStream(); } catch (IOException e1) { e1.printStackTrace(); } Document doc = null; try { doc = reader.read(ins); Element root = doc.getRootElement(); List<Element> list = root.elements(); for (Element e : list) { map.put(e.getName(), e.getText()); } return map; } catch (DocumentException e1) { e1.printStackTrace(); } finally{ ins.close(); } return null; } 3. Convert text message object to xml
/** * Convert text message object to xml * * @param textMessage Text message object* @return xml */ public static String textMessageToXml(TextMessage textMessage){ XStream xstream = new XStream(); xstream.alias("xml", textMessage.getClass()); return xstream.toXML(textMessage); } So far, the work has been completed. At this time, you can try to send "test" in the official account. You will receive a WeChat reply "the text is test". This is also the reply process made in the above code. Of course, you can also use your imagination to do everything you want to do, such as replying to 1 check the weather, 2 checking the violations, etc....
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.