WeChat sharing function development
After a day, I have developed the function of sending WeChat to friends and sharing it to my friends circle. I will share it with you here to avoid detours.
1. Server-side program
package com.wimedia.controller;import java.io.IOException;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Arrays;import java.util.Date;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import com.google.gson.Gson;import com.wimedia.model.Ticket;import com.wimedia.service.ArticleSolrService;import com.wimedia.service.TicketRepository;import com.wimedia.service.TicketRepositorySolr;import com.wimedia.utils.GetRandomStr;import com.wimedia.utils.SignatureBean;import com.wimedia.utils.weixin.WeixinUtil;/** * * * <p>Project:mryl_phone_v2</p> * *<p>Package:com.wimedia.controller</p> * *<p>Description:WeChat Share Controller</p> * *<p>Company:Wimedia</p> * *@Athor:SongJia * *@Date:2016-7-15 09:34:10 am * */@Controller@RequestMapping("/WeixinshareController/Api/Inteface")public class WeixinshareController { @Autowired private TicketRepositorySolr ticketRepositorySolr; @RequestMapping("/getSignature") public String getSignature( HttpServletRequest request, HttpServletResponse response) throws IOException, ParseException{ //Get signature page link String url = request.getParameter("url"); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //Get the tag from the database and check whether the tag expires. Ticket oldticket = ticketRepositorySolr.getTicketById("20160114wiimmediamrylsong1152"); if(oldticket==null){//The first time you access, the tag does not exist. executeTicket(response,"1",url,format); return null; }else{//The tag exists, determine whether the tag is timed out String oldAcquiretime = oldticket.getAcquiretime(); long difference=format.parse(format.format(new Date())).getTime()-format.parse(oldAcquiretime).getTime(); if(difference>71000000){//The tag timeout, go to the WeChat server to request the tag timeout is 7200 seconds (72000000 milliseconds) executeTicket(response,"2",url,format); return null; }else{//The tag has not timed out/** * Notes* 1. The noncestr and timestamp used for signature must be the same as the nonceStr and timestamp in wx.config. * 2. The url used for signature must be the complete URL of the page calling the JS interface. * 3. For security reasons, developers must implement signature logic on the server side. * ****It is easy to make errors when configuring the signature according to point 1. You need to pass the noncestr and timestamp that generates the Ticket to the client*** */ String signature = signature(oldticket.getTicket(),oldticket.getTimestamp(),oldticket.getNoncestr(),url); SignatureBean signatureBean = new SignatureBean(); signatureBean.setNoncestr(oldticket.getNoncestr()); signatureBean.setSignature(signature); signatureBean.setTimestamp(oldticket.getTimestamp()); signatureBean.setUrl(url); response.setContentType("text/html;charset=UTF-8"); response.getWriter().print(new Gson().toJson(signatureBean)); return null; } } } /** * *<p>Project:mryl_phone_v2</p> * *<p>:mryl_phone_v2</p> * *<p>Description: The method to update and get tickets. Because the solr is used, the update is the same as the new one. If there is no ID, it will be added. If it is responsible, update</p> * *<p>Company:Wiimedia</p> * *@Athor:SongJia * *@Date:2016-7-15 09:45:00 am * */ public void executeTicket(HttpServletResponse response,String flag,String url,SimpleDateFormat format) throws IOException{ //Get the signature and then the string GetRandomStr randomStr = new GetRandomStr(); String noncestr = randomStr.getRandomString(15); //Get the signature timestamp String timestamp = Long.toString(System.currentTimeMillis()); //Request accessToken String accessTokenUrl ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=your APPID&secret=your key"; String tokenJson = WeixinUtil.httpRequest(accessTokenUrl, "GET", null); Gson gson = new Gson(); ShareAccess_Token token = gson.fromJson(tokenJson, ShareAccess_Token.class); String to= token.getAccess_token(); //Get the tag String urlTicket ="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+to+"&type=jsapi"; String ticketJson = WeixinUtil.httpRequest(urlTicket, "GET", null); Ticket ticket = gson.fromJson(ticketJson, Ticket.class); String t = ticket.getTicket(); //String uuid = UUID.randomUUID().toString().trim().replaceAll("-", ""); //My Ticket ID is a dead String acquisition time = format.format(new Date()); ticket.setTid("20160114wiimmediamrylsong1152"); ticket.setAcquiretime(acquiretime); ticket.setTimestamp(timestamp); ticket.setNoncestr(noncestr); //Because the SOLR is used, the method of updating and adding is the same. You can modify it according to your specific needs. This article will no longer post code. if(flag.equals("2")){ ticketRepositorySolr.addTicketToSolr(ticket); }else{ ticketRepositorySolr.addTicketToSolr(ticket); } /** * Notes* 1. The noncestr and timestamp used for signature must be the same as the nonceStr and timestamp in wx.config. * 2. The url used for signature must be the complete URL of the page calling the JS interface. * 3. For security reasons, developers must implement signature logic on the server side. * *According to point 1, it is easy to make errors when configuring the signature. You need to pass the noncestr and timestamp that generates the Ticket to the client* */ String signature = signature(t,timestamp,noncestr,url); SignatureBean signatureBean = new SignatureBean(); signatureBean.setNoncestr(noncestr); signatureBean.setSignature(signature); signatureBean.setTimestamp(timestamp); signatureBean.setUrl(url); response.setContentType("text/html;charset=UTF-8"); response.getWriter().print(new Gson().toJson(signatureBean)); } /** * *<p>Project:mryl_phone_v2</p> * *<p>:mryl_phone_v2</p> * *<p>Description:Signature based on tags, timestamps, keys, URLs</p> * *<p>Company:Wiimedia</p> * *@Athor:SongJia * *@Date:2016-7-15 09:37:13 am * */ private String signature(String jsapi_ticket, String timestamp, String noncestr, String url) { jsapi_ticket = "jsapi_ticket=" + jsapi_ticket; timestamp = "timestamp=" + timestamp; noncestr = "noncestr=" + noncestr; url = "url=" + url; String[] arr = new String[] { jsapi_ticket, timestamp, noncestr, url }; // Sort dictionary token, timestamp, noncestr, url parameters Arrays.sort(arr); StringBuilder content = new StringBuilder(); for (int i = 0; i < arr.length; i++) { content.append(arr[i]); if (i != arr.length - 1) { content.append("&"); } } MessageDigest md = null; String tmpStr = null; try { md = MessageDigest.getInstance("SHA-1"); // Splice three parameter strings into a string for sha1 encryption byte[] digest = md.digest(content.toString().getBytes()); tmpStr = byteToStr(digest); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } content = null; return tmpStr; } /** * Convert bytes to hexadecimal string* * @param mByte * @return */ private static String byteToHexStr(byte mByte) { char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; char[] tempArr = new char[2]; tempArr[0] = Digit[(mByte >>> 4) & 0X0F]; tempArr[1] = Digit[mByte & 0X0F]; String s = new String(tempArr); return s; } /** * Convert byte array to hexadecimal string* * @param byteArray * @return */ private static String byteToStr(byte[] byteArray) { String strDigest = ""; for (int i = 0; i < byteArray.length; i++) { strDigest += byteToHexStr(byteArray[i]); } return strDigest; } class ShareAccess_Token{ private String access_token; private String expires_in; public String getAccess_token() { return access_token; } public void setAccess_token(String accessToken) { access_token = accessToken; } public String getExpires_in() { return expires_in; } public void setExpires_in(String expiresIn) { expires_in = expiresIn; } }}2. Client code.
<script type="text/javascript"> var url = window.location.href; var articleId = ""; var shareTitle="Tomorrow's Medical Information"; var shareImgUrl=""; var userinfo = localStorage.getItem("_userinfo"); var timestamp; var noncestr; var signature; //Get the signature $.ajax({ type: "GET", url: "WeixinshareController/Api/Inteface/getSignature", //data:{timestamp:timestamp,noncestr:noncestr,url:url}, data:{url:url}, success: function(data){ var objData=JSON.parse(data); timestamp=objData.timestamp; noncestr=objData.noncestr; signature=objData.signature; console.log(objData); wxShare(); } }); function wxShare(){ wx.config({ debug: false, // Turn on debug mode, the return values of all APIs called will be alerted on the client. To view the passed parameters, you can open them on the PC side. The parameter information will be printed through the log and will only be printed on the PC side. AppId: 'Your appid', // It is the same as the one who gets Tickke-------required, the unique identifier of the official account timestamp:timestamp, // Required, generate the timestamp of the signature nonceStr: noncestr, // Required, generate the random string of signature signature: signature, // Required, signing, see Appendix 1 jsApiList: [ 'onMenuShareAppMessage' ] // Required, list of JS interfaces to be used, and list of all JS interfaces is shown in Appendix 2 }); } wx.ready(function(){ //After config information verification, the ready method will be executed. All interface calls must be obtained after the config interface obtains the result. //config is an asynchronous operation of a client. Therefore, if you need to call the relevant interface when the page is loaded, the relevant // interface must be called in the ready function to ensure correct execution. For interfaces that are called only when the user triggers, they can be called directly without putting them in the ready function. //-------------"Share to friends" wx.onMenuShareAppMessage({ title: "Tomorrow's Medical Information", // Share title desc: shareTitle, // Share description link: url, // Share link imgUrl: shareImgUrl, // Share icon type: '', // Share type, music, video or link, do not fill in the default link dataUrl: '', // If the type is music or video, you need to provide a data link, default is empty success: function () { // Callback function executed after the user confirms the sharing, }, cancel: function () { // Callback function executed after the user cancels the sharing} }); //-----------------------"Share to friends" wx.onMenuShareTimeline({ title: 'Tomorrow Medical Information', // Share title link: '', // Share link imgUrl: shareImgUrl, // Share icon success: function () { // Callback function executed after user confirms sharing}, cancel: function () { // Callback function executed after user cancels sharing} }); //--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- The callback function executed after the user confirms the sharing}, cancel: function () { // The callback function executed after the user cancels the sharing} }); //--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------3. The tools and models required by the server
① Ticket
package com.wimedia.model;public class Ticket{ private String tid; private String ticket; private String errcode; private String errmsg; private String expires_in; private String acquisitiontime; private String noncestr; private String timestamp; public Ticket(String tid, String ticket, String errcode, String errmsg, String expiresIn, String acquisitiontime, String noncestr, String timestamp) { super(); this.tid = tid; this.ticket = ticket; this.errcode = errcode; this.errmsg = errmsg; expires_in = expiresIn; this.acquiretime = acquiretime; this.noncestr = noncestr; this.timestamp = timestamp; } public String getTid() { return tid; } public void setTid(String tid) { this.tid = tid; } public String getTicket() { return ticket; } public void setTicket(String ticket) { this.ticket = ticket; } public String getErrcode() { return errcode; } public void setErrcode(String errcode) { this.errcode = errcode; } public String getErrmsg() { return errmsg; } public void setErrmsg(String errmsg) { this.errmsg = errmsg; } public String getExpires_in() { return expires_in; } public void setExpires_in(String expiresIn) { expires_in = expiresIn; } public String getAcquiretime() { return acquisitiontime; } public void setAcquiretime(String acquiretime) { this.acquiretime = acquiretime; } public String getNoncestr() { return noncestr; } public void setNoncestr(String noncestr) { this.noncestr = noncestr; } public String getTimestamp() { return timestamp; } public void setTimestamp(String timestamp) { this.timestamp = timestamp; }} ② The business added to the database is implemented according to your needs.
③ GetRandomStr
package com.wiimedia.utils;import java.util.Random;public class GetRandomStr { /** * *<p>Project:mryl_phone_v2</p> * *<p>:mryl_phone_v2</p> * *<p>Description:Generate an instant string</p> * *<p>Company:Wiimedia</p> * *@Athor:SongJia * *@Date:2016-7-14 11:14:46 am * */ public String getRandomString(int length) { String base = "abcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < length; i++) { int number = random.nextInt(base.length()); sb.append(base.charAt(number)); } return sb.toString(); }}④ SignatureBean
package com.wimedia.utils;public class SignatureBean { private String noncestr; private String url; private String timestamp; private String signature; public String getNoncestr() { return noncestr; } public void setNoncestr(String noncestr) { this.noncestr = noncestr; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getTimestamp() { return timestamp; } public void setTimestamp(String timestamp) { this.timestamp = timestamp; } public String getSignature() { return signature; } public void setSignature(String signature) { this.signature = signature; }}⑤ WeixinUtil
package com.wimedia.utils.weixin;import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.net.ConnectException;import java.net.URL;import javax.net.ssl.HttpsURLConnection;import javax.net.ssl.SSLContext;import javax.net.ssl.SSLSocketFactory;import javax.net.ssl.TrustManager;/** * *<p>Project:mryl_phone_v2</p> * *<p>:mryl_phone_v2</p> * *<p>Description:Public Platform Interface Tool Class</p> * *<p>Company:Wiimedia</p> * *@Athor:SongJia * *@Date:2016-7-15 09:37:13 am * */public class WeixinUtil { /** * Initiate https request and get the result* * @param requestUrl Request Address* @param requestMethod Request Method (GET, POST) * @param outputStr Data submitted* @return JSONObject(get the attribute value of the json object through JSONObject.get(key)) */ public static String httpRequest(String requestUrl, String requestMethod, String outputStr) { StringBuffer buffer = new StringBuffer(); try { // Create SSLContext object and initialize TrustManager[] tm = { new MyX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); // Get the SSLSocketFactory object from the above SSLContext object SSLSocketFactory ssf = sslContext.getSocketFactory(); URL url = new URL(requestUrl); HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection(); httpUrlConn.setSSLSocketFactory(ssf); httpUrlConn.setDoOutput(true); httpUrlConn.setDoInput(true); httpUrlConn.setUseCaches(false); // Set request method (GET/POST) httpUrlConn.setRequestMethod(requestMethod); if ("GET".equalsIgnoreCase(requestMethod)) httpUrlConn.connect(); // When there is data that needs to be submitted if (null != outputStream outputStream = httpUrlConn.getOutputStream(); // Pay attention to the encoding format to prevent Chinese garbled outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // Convert the returned input stream into a string InputStream inputStream = httpUrlConn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // Release the resource inputStream.close(); inputStream = null; httpUrlConn.disconnect(); return buffer.toString(); } catch (ConnectException ce) { ce.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return ""; }}4. At this point, the sharing function has been developed, but you will encounter many problems when generating signature. Here are some troubleshooting methods for wx.config failures.
① Confirm whether the generated signature is correct in the http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign provided by WeChat for verification
② Whether the noncestr, timestamp used in wx.config is consistent with the corresponding noncestr and timestamp used to sign... As above (1. Server code)
(It is possible that due to the JS page loading order problem, the server-generated signature, noncestr, and timestamp were not obtained in wx.config).
③ Confirm that the url is the complete url of the page, including the GET parameter part that needs to be removed from the following #
④ Is the appid in config consistent with the appid used to obtain jsapi_ticket?
⑤ Error {errmsg:config:ok} is the normal return of debugging mode and turn off debug mode. OK
wx.config debug: false,
This article has been compiled into "Android WeChat Development Tutorial Summary", and "Java WeChat Development Tutorial Summary" welcomes everyone to learn and read.
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.