剛進入一家新公司,接到的第一個任務就是須要須要自定義微信分享的效果(自定義縮略圖,標題,摘要),一開始真是一臉懵逼,在網上搜索了半天以後大概有了方案。值得注意的是一開始搜索到的解決方案全是調用微信的自帶的JS-SDK,然而騰訊是不會讓廣大吃瓜羣衆這麼垂手可得的調用他們的東西的。微信開發團隊已經把調用的權限收回,如今沒法直接在頁面直接調用JS-SDK了。話很少說,直接上乾貨。javascript
原始的分享效果:html
使用微信JS-SDK的分享效果:java
能夠看出縮略圖,標題,摘要樣式良好,給用戶的體驗很好。web
微信官方開發者文檔地址:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115ajax
如今的思路已經很明確了,就是經過調用微信的JS-SDK實現自定義分享效果。可是這個調用過程比較繁瑣,須要提早準備以下東西:算法
(1)微信服務號一個,而且已經經過了實名認證;chrome
沒有實名認證的話,一些接口沒有調用權限。json
(2)一個ICP備案的域名;api
這個域名須要設置爲微信公衆號後臺的JS接口安全域名,不然微信仍然不容許調用它的接口。數組
這時你們應該就犯難了,這樣的話豈不是不能在本地測試,只能部署到生產環境才能測試?不用着急,解決方案告訴你們:花生殼的內網穿透服務(收費,20元之內)
花生殼官網:http://hsk.oray.com/price/#personal
選擇我的免費版就能夠了,雖說是免費版,可是其實註冊過程當中仍是要收幾塊錢的,由於我本身買了域名和流量因此花的錢更多一些,但也在20元之內。不建議你們購買流量,送的流量能夠用好久了。
當準備好上面提到的就能夠開始敲代碼了。
(3)安裝微信開發者工具,用於本地調試。
下載地址:https://mp.weixin.qq.com/debug/cgi-bin/webdebugger/download?from=mpwiki&os=x64
官方使用教程:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1455784140
進入微信後臺,找到下面的菜單
獲取AppID和AppSecret
設置JS接口安全域名
注意第三步,若是微信服務器不能在咱們的服務器上訪問到這個txt文件,域名是沒法設置成功的,這裏先告訴你們在哪裏設置,想要成功設置域名還須要使用花生殼的服務,讓微信服務器訪問咱們本地工程中的的txt文件才行。
hkh3321313.vicp.io是在花生殼上購買的域名,免費送的域名是在太難記了,徹底不能忍。
這裏須要注意是http仍是https,若是生產環境是https,務必前綴是https,都則會出現mix content這樣的錯誤,致使引入失敗。
<script typet="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
由於獲取這兩個參數的次數是有限制的(accessToke 每日2000次,jsapi_ticket 每日100000次),有效期是7200秒,每兩小時請求一次就行啦,把獲取的accessToke和jsapi_ticket保存在後臺,因此accessToken和jsapi_ticket這兩個參數的獲取是經過ajax方式請求後臺,而不是實時去獲取的。
config幾個參數須要詳細說明一下:
附上signature算法的官方說明:
https://mp.weixin.qq.com/wiki?action=doc&id=mp1421141115&t=0.15697429783636763#buzhou3
在附錄1中能夠找到詳細說明。
此外,官方提供了一個簽名算法的校驗工具:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign
下面只附上了主要的方法:
//獲取accessToken private JSONObject getAccessToken(){ //String accessTokenUrl= https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET String requestUrl = accessTokenUrl.replace("APPID",appId).replace("APPSECRET",appSecret); log.info("getAccessToken.requestUrl====>"+requestUrl); JSONObject result = HttpUtil.doGet(requestUrl); return result ; } //獲取ticket private JSONObject getJsApiTicket(){ //String apiTicketUrl= https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi String requestUrl = apiTicketUrl.replace("ACCESS_TOKEN", accessToken); log.info("getJsApiTicket.requestUrl====>"+requestUrl); JSONObject result = HttpUtil.doGet(requestUrl); return result; } //生成微信權限驗證的參數 public Map<String, String> makeWXTicket(String jsApiTicket, String url) { Map<String, String> ret = new HashMap<String, String>(); String nonceStr = createNonceStr(); String timestamp = createTimestamp(); String string1; String signature = ""; //注意這裏參數名必須所有小寫,且必須有序 string1 = "jsapi_ticket=" + jsApiTicket + "&noncestr=" + nonceStr + "×tamp=" + timestamp + "&url=" + url; log.info("String1=====>"+string1); try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(string1.getBytes("UTF-8")); signature = byteToHex(crypt.digest()); log.info("signature=====>"+signature); } catch (NoSuchAlgorithmException e) { log.error("WeChatController.makeWXTicket=====Start"); log.error(e.getMessage(),e); log.error("WeChatController.makeWXTicket=====End"); } catch (UnsupportedEncodingException e) { log.error("WeChatController.makeWXTicket=====Start"); log.error(e.getMessage(),e); log.error("WeChatController.makeWXTicket=====End"); } ret.put("url", url); ret.put("jsapi_ticket", jsApiTicket); ret.put("nonceStr", nonceStr); ret.put("timestamp", timestamp); ret.put("signature", signature); ret.put("appid", appId); return ret; } //字節數組轉換爲十六進制字符串 private static String byteToHex(final byte[] hash) { Formatter formatter = new Formatter(); for (byte b : hash) { formatter.format("%02x", b); } String result = formatter.toString(); formatter.close(); return result; } //生成隨機字符串 private static String createNonceStr() { return UUID.randomUUID().toString(); } //生成時間戳 private static String createTimestamp() { return Long.toString(System.currentTimeMillis() / 1000); }
HttpUtil代碼
public class HttpUtil { public static Log logger = LogFactory.getLog(HttpUtil.class); //get請求 public static com.alibaba.fastjson.JSONObject doGet(String requestUrl) { CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String responseContent = null; com.alibaba.fastjson.JSONObject result = null; try { //建立Get請求, HttpGet httpGet = new HttpGet(requestUrl); //執行Get請求, response = httpClient.execute(httpGet); //獲得響應體 HttpEntity entity = response.getEntity(); //獲取響應內容 responseContent = EntityUtils.toString(entity,"UTF-8"); //轉換爲map result = JSON.parseObject(responseContent); } catch (IOException e) { logger.error("HttpUtil=====Start"); logger.error(e.getMessage(),e); logger.error("HttpUtil=====End"); } return result; } }
官方示例:
wx.config({ debug: true, // 開啓調試模式,調用的全部api的返回值會在客戶端alert出來,若要查看傳入的參數,能夠在pc端打開,參數信息會經過log打出,僅在pc端時纔會打印。 appId: '', // 必填,公衆號的惟一標識
timestamp: , // 必填,生成簽名的時間戳 nonceStr: '', // 必填,生成簽名的隨機串
signature: '',// 必填,簽名,見附錄1 jsApiList: [] // 必填,須要使用的JS接口列表,全部JS接口列表見附錄2 });
本身的代碼:
其中的url不能硬編碼寫在後臺,必須經過動態傳遞。
$(function(){ var url = location.href.split('#').toString();//url不能寫死 $.ajax({ type : "get", url : "/wechatParam", dataType : "json", async : false, data:{url:url}, success : function(data) { wx.config({ debug: false,////生產環境須要關閉debug模式 appId: data.appid,//appId經過微信服務號後臺查看 timestamp: data.timestamp,//生成簽名的時間戳 nonceStr: data.nonceStr,//生成簽名的隨機字符串 signature: data.signature,//簽名 jsApiList: [//須要調用的JS接口列表 'checkJsApi',//判斷當前客戶端版本是否支持指定JS接口 'onMenuShareTimeline',//分享給好友 'onMenuShareAppMessage'//分享到朋友圈 ] }); }, error: function(xhr, status, error) { //alert(status); //alert(xhr.responseText); } }) });
官方示例:
wx.ready(function(){ // config信息驗證後會執行ready方法,全部接口調用都必須在config接口得到結果以後,config是一個客戶端的異步操做,因此若是須要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。對於用戶觸發時才調用的接口,則能夠直接調用,不須要放在ready函數中。 });
本身的代碼:
wx.ready(function () { var link = window.location.href; var protocol = window.location.protocol; var host = window.location.host; //分享朋友圈 wx.onMenuShareTimeline({ title: '這是一個自定義的標題!', link: link, imgUrl: protocol+'//'+host+'/resources/images/icon.jpg',// 自定義圖標 trigger: function (res) { // 不要嘗試在trigger中使用ajax異步請求修改本次分享的內容,由於客戶端分享操做是一個同步操做,這時候使用ajax的回包會尚未返回. //alert('click shared'); }, success: function (res) { //alert('shared success'); //some thing you should do }, cancel: function (res) { //alert('shared cancle'); }, fail: function (res) { //alert(JSON.stringify(res)); } }); //分享給好友 wx.onMenuShareAppMessage({ title: '這是一個自定義的標題!', // 分享標題 desc: '這是一個自定義的描述!', // 分享描述 link: link, // 分享連接,該連接域名或路徑必須與當前頁面對應的公衆號JS安全域名一致 imgUrl: protocol+'//'+host+'/resources/images/icon.jpg', // 自定義圖標 type: 'link', // 分享類型,music、video或link,不填默認爲link dataUrl: '', // 若是type是music或video,則要提供數據連接,默認爲空 success: function () { // 用戶確認分享後執行的回調函數 }, cancel: function () { // 用戶取消分享後執行的回調函數 } }); wx.error(function (res) { alert(res.errMsg); }); });
到這裏全部的代碼都已經分享完畢了。
這個基本是傻瓜式的,只要下載他們的客戶端就能夠了。
官網教程:http://hsk.oray.com/news/4345.html
添加一個映射就能夠了
把以前下載的txt文件放在工程目錄webapp下,而後本地啓動工程,肯定經過域名能夠訪問本地項目後,設置JS安全域名
如今訪問 域名:端口號(例如:hkh3321313.vicp.io:8080)就能夠訪問本地項目啦。
微信開發者工具其實就是微信的瀏覽器,其中集成了chrome的調試工具,前面提到wx.config中的debug模式這裏就發揮做用了,瀏覽器會自動彈出調用微信接口的返回結果。
成功返回的話結果應該是ok什麼的,圖就不上了。提醒你們,上生產環境必定要把debug改成false~
雖然已經給了主要的代碼,你們必定仍是不想寫接口,下面附上完整的代碼,若是你以爲解了燃眉之急,就點個頂吧,哈哈哈~
@Controller public class WeChatController { private final Logger log = LoggerFactory.getLogger(this.getClass()); //獲取相關的參數,在application.properties文件中 @Value("${wechat.appId}") private String appId; @Value("${wechat.appSecret}") private String appSecret; @Value("${wechat.url.accessToken}") private String accessTokenUrl; @Value("${wechat.url.apiTicket}") private String apiTicketUrl; //微信參數 private String accessToken; private String jsApiTicket; //獲取參數的時刻 private Long getTiketTime = 0L; private Long getTokenTime = 0L; //參數的有效時間,單位是秒(s) private Long tokenExpireTime = 0L; private Long ticketExpireTime = 0L; //獲取微信參數 @RequestMapping("/wechatParam") @ResponseBody public Map<String, String> getWechatParam(String url){ //當前時間 long now = System.currentTimeMillis(); log.info("currentTime====>"+now+"ms"); //判斷accessToken是否已經存在或者token是否過時 if(StringUtils.isBlank(accessToken)||(now - getTokenTime > tokenExpireTime*1000)){ JSONObject tokenInfo = getAccessToken(); if(tokenInfo != null){ log.info("tokenInfo====>"+tokenInfo.toJSONString()); accessToken = tokenInfo.getString("access_token"); tokenExpireTime = tokenInfo.getLongValue("expires_in"); //獲取token的時間 getTokenTime = System.currentTimeMillis(); log.info("accessToken====>"+accessToken); log.info("tokenExpireTime====>"+tokenExpireTime+"s"); log.info("getTokenTime====>"+getTokenTime+"ms"); }else{ log.info("====>tokenInfo is null~"); log.info("====>failure of getting tokenInfo,please do some check~"); } } //判斷jsApiTicket是否已經存在或者是否過時 if(StringUtils.isBlank(jsApiTicket)||(now - getTiketTime > ticketExpireTime*1000)){ JSONObject ticketInfo = getJsApiTicket(); if(ticketInfo!=null){ log.info("ticketInfo====>"+ticketInfo.toJSONString()); jsApiTicket = ticketInfo.getString("ticket"); ticketExpireTime = ticketInfo.getLongValue("expires_in"); getTiketTime = System.currentTimeMillis(); log.info("jsApiTicket====>"+jsApiTicket); log.info("ticketExpireTime====>"+ticketExpireTime+"s"); log.info("getTiketTime====>"+getTiketTime+"ms"); }else{ log.info("====>ticketInfo is null~"); log.info("====>failure of getting tokenInfo,please do some check~"); } } //生成微信權限驗證的參數 Map<String, String> wechatParam= makeWXTicket(jsApiTicket,url); return wechatParam; } //獲取accessToken private JSONObject getAccessToken(){ //String accessTokenUrl = https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET String requestUrl = accessTokenUrl.replace("APPID",appId).replace("APPSECRET",appSecret); log.info("getAccessToken.requestUrl====>"+requestUrl); JSONObject result = HttpUtil.doGet(requestUrl); return result ; } //獲取ticket private JSONObject getJsApiTicket(){ //String apiTicketUrl = https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi String requestUrl = apiTicketUrl.replace("ACCESS_TOKEN", accessToken); log.info("getJsApiTicket.requestUrl====>"+requestUrl); JSONObject result = HttpUtil.doGet(requestUrl); return result; } //生成微信權限驗證的參數 public Map<String, String> makeWXTicket(String jsApiTicket, String url) { Map<String, String> ret = new HashMap<String, String>(); String nonceStr = createNonceStr(); String timestamp = createTimestamp(); String string1; String signature = ""; //注意這裏參數名必須所有小寫,且必須有序 string1 = "jsapi_ticket=" + jsApiTicket + "&noncestr=" + nonceStr + "×tamp=" + timestamp + "&url=" + url; log.info("String1=====>"+string1); try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(string1.getBytes("UTF-8")); signature = byteToHex(crypt.digest()); log.info("signature=====>"+signature); } catch (NoSuchAlgorithmException e) { log.error("WeChatController.makeWXTicket=====Start"); log.error(e.getMessage(),e); log.error("WeChatController.makeWXTicket=====End"); } catch (UnsupportedEncodingException e) { log.error("WeChatController.makeWXTicket=====Start"); log.error(e.getMessage(),e); log.error("WeChatController.makeWXTicket=====End"); } ret.put("url", url); ret.put("jsapi_ticket", jsApiTicket); ret.put("nonceStr", nonceStr); ret.put("timestamp", timestamp); ret.put("signature", signature); ret.put("appid", appId); return ret; } //字節數組轉換爲十六進制字符串 private static String byteToHex(final byte[] hash) { Formatter formatter = new Formatter(); for (byte b : hash) { formatter.format("%02x", b); } String result = formatter.toString(); formatter.close(); return result; } //生成隨機字符串 private static String createNonceStr() { return UUID.randomUUID().toString(); } //生成時間戳 private static String createTimestamp() { return Long.toString(System.currentTimeMillis() / 1000); } }