Estuve ocupado hace un tiempo. Comencé a desarrollar la cuenta oficial de WeChat y leí los documentos desde cero. Pasé muchas trampas. Lo he pasado por eso. Recientemente he considerado hacer algún resumen para poder revisarlo cuando lo desarrollaré en el futuro. También daré una referencia a los estudiantes que trabajan en proyectos relacionados.
1. Ideas
Acceso a WeChat: los mensajes de usuario y los impulsos de eventos requeridos por los desarrolladores iniciarán una solicitud a través del servidor WeChat y reenviarán a la dirección de URL del servidor que está configurado en la plataforma pública. El servidor WeChat traerá los cuatro parámetros de firma, marca de tiempo, nonce y ECHOSTR. Nuestro propio servidor empalma el token configurado por la plataforma pública, así como la marca de tiempo cargada, y Nonce está encriptado en SHA1 y coincide con la firma. Devuelva el ture para indicar que el acceso es exitoso.
Respuesta del mensaje: cuando un usuario envía un mensaje a la cuenta oficial, el servidor WeChat solicitará el mensaje del usuario a la interfaz correspondiente del servidor que configuramos en formato XML a través de la publicación. Lo que tenemos que hacer es realizar el procesamiento lógico correspondiente basado en el tipo de mensaje, etc., y devolver el resultado de retorno final al servidor WeChat a través del formato XML, y la fiesta WeChat lo transmitirá al usuario.
1. Configuración de la plataforma pública
2. controlador
@Controlador @requestmapping ("/weChat") publicClass weChatController {@Value ("$ {dnbx_token}") cadena privada dnbx_token; Private static final logger logger = loggerFactory.getLogger (weChatController.class); @Resource WeCatservice WeChatservice; /** * WeChat Access * @param wc * @return * @throws ioexception */@requestmapping (valor = "/conection", método = {requestmethod.get, requestmethod.post}) @ResponseBody publicVoid ConnectWeixin (httpServletRequest Solicitud, httPserveTretResponse) SETS de la respuesta y la respuesta de la respuesta de la Respuesta y el CODIMIENTO de la respuesta de la respuesta. a UTF-8 (evitar la solicitud china de confusión). SetcharacterEncoding ("UTF-8"); // El servidor WeChat utiliza la codificación UTF-8 cuando los mensajes de publicación, y la misma codificación debe usarse al recibir, de lo contrario, el conflicto chino será; respuesta.setcharacterEncoding ("UTF-8"); // Al responder a los mensajes (responder al usuario), el método de codificación también se establece en UTF-8, el principio es el mismo que el anterior; boolean isget = request.getMethod (). tOlowerCase (). Equals ("get"); PrintWriter out = Response.getWriter (); intente {if (isget) {string firature = requit.getParameter ("firma"); // wechat cifrado firma string timestamp = request.getParameter ("timestamp"); // timestamp string nonce = request.getParameter ("nonce"); // número aleatorio string string echostr = request firma. Si la verificación es exitosa, devuelva el ecostr tal como está, lo que indica que el acceso es exitoso. De lo contrario, el acceso falla si (SignUTIl.CheckSignature (DNBX_Token, Signature, TimeStamp, Nonce)) {logger.info ("Conecte el servidor Weixin es exitoso"); Response.getWriter (). Write (ECHOSTR); } else {logger.error ("¡No se pudo verificar la firma!"); }} else {string respmessage = "¡Mensaje de excepción!"; intente {resphMessage = weChatservice.weixInpost (solicitud); out.write (respessage); Logger.info ("la solicitud completada correctamente"); Logger.info ("To Weixin Server"+RespMessage); } catch (Exception e) {logger.error ("¡No se pudo convertir el mensaje de weixin!"); }}} Catch (Exception e) {logger.error ("Conecte el servidor weixin es un error."); } finalmente {out.close (); }}}3. Firma de verificación de verificación de firma
Desde el controlador anterior, podemos ver que encapsulé un significador de clase de herramientas, llamado firma de verificación y pasé en cuatro valores, dnbx_token, firma, marca de tiempo, nonce. Este proceso es muy importante. De hecho, podemos entenderlo como un proceso de cifrado y descifrado del valor transmitido por WeChat. Para garantizar la seguridad, todas las interfaces en muchos proyectos grandes tendrán tal proceso de verificación. DNBX_TOKen Hemos configurado una cadena de token en la plataforma pública WeChat, ¡mantenga la idea confidencial! Los otros tres son parámetros enviados por el servidor WeChat para enviar una solicitud GET. Realizamos una capa de cifrado SHA1:
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}; // ordenar orden de diccionario de token, marca de tiempo y nonce; // empalme las tres cadenas de parámetros en una cadena para la cadena de cifrado Sha1 tmpStr = sha1.encode (arr [0] + arr [1] + arr [2]); // La cadena cifrada SHA1 se puede comparar con la firma, identificando que la solicitud proviene de WeChat Devuelve TMPSTR! = NULL? tmpstr.equals (firma.touppercase ()): falso; }} 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'}; /*** toma los bytes crudos del resumen y los formatea correctamente. * * @param bytes los bytes sin procesar del resumen. * @return los bytes formateados. */ cadena estática privada getFormattedText (byte [] bytes) {int len = bytes.length; StringBuilder buf = new StringBuilder (len * 2); // Convierta el texto cifrado en una forma de cadena hexadecimal para (int j = 0; j <len; j ++) {buf.append (hex_digits [(bytes [j] >> 4) y 0x0f]); buf.append (hex_digits [bytes [j] & 0x0f]); } return buf.ToString (); } public static string code (String str) {if (str == null) {return null; } try {MessageDigest MessageGest = MessageDigest.getInstance ("Sha1"); MessageDigest.Update (str.getBytes ()); return getFormattedText (MessageDigest.digest ()); } catch (Exception e) {Throw New RuntimeException (e); }}}Cuando lo envíe y lo guarde en la plataforma pública y vea el mensaje verde "éxito de acceso", felicidades por completar el acceso de WeChat. Este proceso requiere un poco más de cuidado y presta atención al caso en el algoritmo de cifrado. Si el acceso no tiene éxito, la mayoría de los casos son problemas con el algoritmo de cifrado, verifíquelo más.
4. Implementar el servicio de respuesta de mensaje automático
/ ** * Solicitudes de proceso de WeChat * * @param solicitud * @return */ public string weixInPost (httpservletRequest request) {string respsMessage = null; Pruebe {// xml solicitud de análisis de análisis <string, string> requestmap = messageUtil.xmltomap (request); // Cuenta de remitente (Open_ID) String fromUSername = requestMap.get ("fromUsername"); // String de cuenta pública tousername = requestMap.get ("tousername"); // Tipo de mensaje String msgtype = requestmap.get ("msgtype"); // Mensaje Content String Content = requestMap.get ("Content"); Logger.info ("fromUsername is:" + fromUsername + ", tousername es:" + tousername + ", msgtype es:" + msgtype); // Mensaje de texto if (msgtype.equals (messageUtil.req_message_type_text)) {// Aquí ejecuta la lógica correspondiente de acuerdo con las palabras clave, solo hay uno en el que no puede pensar, y no hay tal cosa como no puede hacer si (content.equals ("xxx")) {} // respuesta automática textmessage text = newmessage ();););););); text.setContent ("El texto es" + contenido); text.setTouserName (fromUsername); text.setFromUsername (tousername); text.setCreateTime (nueva fecha (). getTime () + ""); text.setMsgType (msgtype); respmessage = MessageUtil.TextMessageToxml (texto); }/*else if (msgtype.equals (messageUtil.req_message_type_event)) {// event push string eventtype = requestmap.get ("event");/type de eventos if (eventtype.equals (messageUtil.event_type_subscribe)) {// suscripción a respectent = "Bienvenido a seguir la cuenta; return Messageresponse.gettextMessage (fromUsername, tousername, respcontent); } else if (eventtype.equals (messageUtil.event_type_click)) {// menú personalizado Haga clic en event String eventKey = requestmap.get ("eventkey"); // valor de tecla de evento, correspondiente al valor de tecla especificado al crear el menú personalizado logger.info ("EventKey es:" +EventKey); return xxx; }} // activar la prueba de reconocimiento de sonido de WeChat 2015-3-30 más if (msgtype.equals ("voz")) {string recvMessage = requestmap.get ("reconocimiento"); // respcontent = "resultado de análisis del habla recibido:"+RecvMessage; if (recvMessage! = null) {respcontent = tulingapiprocess.gettulingResult (recvMessage); } else {respcontent = "Lo que dijiste es demasiado vago, ¿puedes decirlo de nuevo?"; } return Messageresponse.gettextMessage (fromUsername, tousername, respcontent); } // función de fotografía más if (msgtype.equals ("pic_sysphoto")) {} else {return Messageresponse.gettextMessage (fromUsername, tousername, "volver a vacío"); }*/ // Event Push Else if (msgtype.equals (messageUtil.req_message_type_event)) {string eventType = requestmap.get ("event");/ type de eventos // suscripción if (eventtype.equals (messageUtil.event_type_subscribe)) {textMessage text = newexsage (); text.setContent ("Bienvenido a seguir, xxx"); text.setTouserName (fromUsername); text.setFromUsername (tousername); text.setCreateTime (nueva fecha (). getTime () + ""); text.setmsgtype (MessageUtil.Resp_Message_Type_Text); respmessage = MessageUtil.TextMessageToxml (texto); } // TODO Después de la suscripción, el usuario no puede recibir el mensaje enviado por la cuenta oficial, por lo que no es necesario responder al mensaje más if (eventType.equals (messageUtil.event_type_unsubscribe)) {// sausscribe} // personalizado menú de eventos si (eventtype.equals (messageUtil.event_typee_cLick) {strike keykey {string keykey) requestMap.get ("EventKey"); // El valor de clave de evento corresponde al valor de clave especificado al crear un menú personalizado if (eventKey.equals ("custom_telephone")) {textMessage text = new TextMessage (); text.setContent ("0755-86671980"); text.setTouserName (fromUsername); text.setFromUsername (tousername); text.setCreateTime (nueva fecha (). getTime () + ""); text.setmsgtype (MessageUtil.Resp_Message_Type_Text); respmessage = MessageUtil.TextMessageToxml (texto); }}}} capt (excepción e) {logger.error ("error ...")} return respmessage; }El código se publica como se indicó anteriormente. La mayoría de ellos tienen comentarios. Si lee la semántica básica una vez, no necesita explicarlos.
Hay un lugar que necesita atención especial:
El nombre rojo del nombre y el nombre de tousername son exactamente lo contrario, que también es una de las trampas. Recuerdo que lo había ajustado durante mucho tiempo, pero no fue un problema, pero no estaba funcionando. ¡Finalmente, recibí el mensaje después de cambiar estos dos! De hecho, es correcto pensarlo. Cuando regrese al servidor WeChat, su rol cambiará, por lo que el remitente y el receptor son definitivamente lo contrario.
5. Messageutil
MessageUtil de clase pública { / *** Tipo de mensaje de retorno: Text* / public static final String resp_message_type_text = "text"; / *** Tipo de mensaje de retorno: música*/ public static final String resp_message_type_music = "Music"; / *** Tipo de mensaje de retorno: Texto gráfico*/ public static final String resp_message_type_news = "News"; / *** Tipo de mensaje de solicitud: Text*/ public static final String req_message_type_text = "text"; / *** Tipo de mensaje de solicitud: imagen*/ public static final String req_message_type_image = "imagen"; / *** Tipo de mensaje de solicitud: enlace*/ public static final String req_message_type_link = "enlace"; / *** Tipo de mensaje de solicitud: ubicación geográfica*/ public static final String req_message_type_location = "ubicación"; / *** Tipo de mensaje de solicitud: audio*/ public static final String req_message_type_voice = "Voice"; / ** * Tipo de mensaje de solicitud: Push */ public static final String req_message_type_event = "evento"; / ** * Tipo de evento: suscríbete (suscríbete) */ public static final string event_type_subscribe = "suscríbete"; / ** * Tipo de evento: Unsubscribe (Unsubscribe) */ public static final String event_type_unsubscribe = "Unsubscribe"; / ** * Tipo de evento: haga clic (Menú personalizado haga clic en evento) */ public static final String event_type_click = "haga clic"; }Aquí, para mejorar la legibilidad y la escalabilidad del programa, he hecho algunas encapsulación, definí varias constantes y encapsulé algunos parámetros que pasaron de WeChat a Java Bean objetos persistentes. El código central es el anterior. Concéntrese en la conversión entre XML y MAP
De hecho, este problema se atribuye a WeChat utilizando la comunicación XML, y generalmente usamos JSON, por lo que podemos sentirnos un poco incómodos en un corto período de tiempo.
1. PAQUETO DE INSTRODUCE
<
2.xml para mapear el objeto de colección
/ ** * xml para mapear * @param request * @return * @throws ioexception */ @supplesswarnings ("sin verificar") public static map <String, String> xmltomap (httpservletRequest solicitud) lanza ioexception {map <string, string> map = new Hashmap <string, string> (); Saxreader lector = new saxreader (); InputStream INS = NULL; intente {ins = request.getInputStream (); } catch (ioException e1) {e1.printstackTrace (); } Documento doc = null; intente {doc = Reader.read (ins); Elemento root = doc.getRootElement (); List <ememem> list = root.elements (); for (elemento e: list) {map.put (e.getName (), e.gettext ()); } mapa de retorno; } catch (DocumentException e1) {e1.printstackTrace (); } finalmente {ins.close (); } return null; } 3. Convertir el objeto de mensaje de texto a XML
/ ** * Convertir el objeto de mensaje de texto a xml * * @param textMessage Mensaje de texto Objeto * @return xml */ public static string textMessageToxml (textMessage textMessage) {xstream xstream = new xStream (); xstream.alias ("xml", textMessage.getClass ()); return xstream.toxml (textMessage); } Hasta ahora, el trabajo se ha completado. En este momento, puede intentar enviar "prueba" en la cuenta oficial. Recibirá una respuesta de WeChat "El texto es prueba". Este es también el proceso de respuesta realizado en el código anterior. Por supuesto, también puede usar su imaginación para hacer todo lo que desea hacer, como responder a 1 verificar el clima, 2 verificar las violaciones, etc.
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.