微信自定义分享链接内容,wx.updateAppMessageShareData、wx.updateTimelineShareData、wx.onMenuShareTimeline
微信分享自定義鏈接
- 目錄
- 問題與需求
- 準備
- 公眾號設置
- 代碼
- 后端代碼(完整代碼在附錄)
- application.properties文件配置
- (1)獲取accessToken
- (2)獲取jsapiTicket
- (3)生成簽名
- 生成wx.config
- 接口返回wx.config
- 前端代碼(完整代碼在附錄)
- 測試
- 附錄
- 后端完整代碼
- 微信工具類.java
- 網絡工具類.java
- controler交互
- js
- 常見問題
- 獲取JSSDK權限成功但分享無效果
- 本地調試無效果
- 微信頁面緩存
目錄
問題與需求
問題:
需求:
準備
認證的公眾號或者服務號(記住開發者ID、開發者密碼)
備案過的域名
公眾號設置
設置-開發-基本配置-IP白名單-添加網站服務器IP地址
設置-公眾號設置-功能設置-設置業務域名與JS接口安全域名(即網站的域名)(這里需要加入認證TXT文件到項目)
代碼
后端代碼(完整代碼在附錄)
根據官方文檔,可以得知大步驟為->綁定域名->引入JS文件->通過config接口注入權限驗證配置。
也就是說后端只要返回wx.config所需參數即可。
wx.config({debug: true, // 開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會打印。appId: '', // 必填,公眾號的唯一標識timestamp: , // 必填,生成簽名的時間戳nonceStr: '', // 必填,生成簽名的隨機串signature: '',// 必填,簽名jsApiList: [] // 必填,需要使用的JS接口列表 });除了簽名,其他參數都可直接獲取或生成,查閱官方簽名文檔,可知需要先生成jsapi_ticket,然后再根據文檔需求生成簽名。
根據文檔可知生成簽名步驟:獲取accessToken -> 生成jsapi_ticket ->簽名算法 -> sha1 加密
application.properties文件配置
#微信配置 WX_APPID=xxxx #appId WX_APPSECRET=xxxxx #密鑰 WX_GRANTTYPE=client_credential(1)獲取accessToken
@Scheduled(initialDelay = 1000, fixedDelay = 100*60*1000)public String getAccessToken() {Map<String,String> resultMap=new HashMap<>();//獲取access_tokenString url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}";//設置參數Map<String, Object> map = new HashMap<>();map.put("appid", WX_APPID);map.put("secret", WX_APPSECRET);//發送get請求String accessTokenResult = ((Map<String, String>) JSON.parse(httpUtil.GETclient(url, map))).get("access_token");tokenAll = accessTokenResult;return accessTokenResult;}(2)獲取jsapiTicket
@Scheduled(initialDelay = 1000, fixedDelay = 100*60*1000)public String getJsapiTicket() {Map<String,Object> map=new HashMap<>();String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={ACCESS_TOKEN}&type=jsapi";//設置參數map.put("ACCESS_TOKEN", getAccessToken());//發送get請求String ticket = ((Map<String, String>) JSON.parse(httpUtil.GETclient(url, map))).get("ticket");ticketAll = ticket;return ticket;}(3)生成簽名
這個方法是參考官方的簽名算法文檔
/*** 簽名* @return*/public Map<String, String> sign(String jsapi_ticket, String url){Map<String,String> ret = new HashMap<String, String>();String nonce_str = create_nonce_str();String timestamp = create_timestamp();String stringAppend;String signature = "";//注意這里參數名必須全部小寫,且必須有序stringAppend = "jsapi_ticket=" + jsapi_ticket +"&noncestr=" + nonce_str +"×tamp=" + timestamp +"&url=" + url;System.out.println(stringAppend);try{MessageDigest crypt = MessageDigest.getInstance("SHA-1");crypt.reset();crypt.update(stringAppend.getBytes("UTF-8"));signature = byteToHex(crypt.digest());}catch (NoSuchAlgorithmException e){e.printStackTrace();}catch (UnsupportedEncodingException e){e.printStackTrace();}ret.put("url", url);ret.put("jsapi_ticket", jsapi_ticket);ret.put("nonceStr", nonce_str);ret.put("timestamp", timestamp);ret.put("signature", signature);ret.put("appId", WX_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;} /*** 隨機字符串*/public String create_nonce_str() {return UUID.randomUUID().toString();}/*** 時間戳* @return*/private static String create_timestamp() {return Long.toString(System.currentTimeMillis() / 1000);}生成wx.config
public Map<String,String> wxConfig(String url) {String jsapi_ticket = ticketAll;// 注意 URL 一定要動態獲取,不能 hardcodeMap<String, String> ret = sign(jsapi_ticket, url);for (Map.Entry entry : ret.entrySet()) {System.out.println(entry.getKey() + ", " + entry.getValue());}return ret;}接口返回wx.config
/*** 獲取wechatConfig信息* @param url 訪問頁面的地址* @return*/@GetMapping("/getWechatConfig")public Map<String,String> getWechatConfig(String url){return wechatUtil.wxConfig(url);}前端代碼(完整代碼在附錄)
分享接口文檔
引入JS文件
JS代碼
//將url 存進緩存下 var url = "https://" + window.location.host; //服務器的url var urlCureent = encodeURIComponent(location.href); //當前頁面的url $.ajax({async:true,dataType:"json",type:"GET",url: url + "/verification/getWechatConfig?url=" + urlCureent,contentType: "application/json; charset=utf-8",success:function(data){wx.config({debug: 0,appId: data.appId,timestamp: data.timestamp,nonceStr: data.nonceStr,signature: data.signature,jsApiList: ["updateAppMessageShareData", "updateTimelineShareData","onMenuShareTimeline","onMenuShareAppMessage"]});wx.ready(function () { //需在用戶可能點擊分享按鈕前就先調用wx.updateAppMessageShareData({title: cardVo.cardName + "個人名片", // 分享標題desc: cardVo.cardNameEn + "'s card", // 分享描述link: location.href, // 分享鏈接,該鏈接域名或路徑必須與當前頁面對應的公眾號JS安全域名一致imgUrl: url + cardVo.cardImg, // 分享圖標success: function () {console.log("success");// 設置成功}});wx.updateTimelineShareData({title: cardVo.cardName + "個人名片", // 分享標題link: location.href, // 分享鏈接,該鏈接域名或路徑必須與當前頁面對應的公眾號JS安全域名一致imgUrl: url + cardVo.cardImg, // 分享圖標success: function () {console.log("success2");// 設置成功}});wx.onMenuShareTimeline({title: cardVo.cardName + "個人名片", // 分享標題desc: cardVo.cardNameEn + "'s card", // 分享描述link: location.href, // 分享鏈接,該鏈接域名或路徑必須與當前頁面對應的公眾號JS安全域名一致imgUrl: url + cardVo.cardImg, // 分享圖標});wx.onMenuShareAppMessage({title: cardVo.cardName + "個人名片", // 分享標題link: location.href, // 分享鏈接,該鏈接域名或路徑必須與當前頁面對應的公眾號JS安全域名一致imgUrl: url + cardVo.cardImg, // 分享圖標});});}});測試
可以看到已經成功獲取JSSDK 權限
附錄
后端完整代碼
微信工具類.java
import com.alibaba.fastjson.JSON; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.PropertySource; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component;import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.Formatter;/*** @program:gzdm* @author:wihenne* @creatTime:2021/05/05**/ @Component @PropertySource({"classpath:application.properties"}) public class WechatUtil {@Value("${WX_APPID}")String WX_APPID;@Value("${WX_APPSECRET}")String WX_APPSECRET;@Value("${WX_GRANTTYPE}")String WX_GRANTTYPE;@AutowiredHttpUtil httpUtil;public static String tokenAll; //微信公眾號的accessToken對象,由于請求次數有限制,這里使用全局靜態變量保存起來public static String ticketAll;//使用全局靜態變量存儲ApiTicket對象,當然如果使用緩存框架保存當然更好,這邊只是做一個簡單示例//用于下面返回隨機字符串的函數private final static String string = "0123456789";final private static char[] chars = string.toCharArray();/*** 獲取公眾號的ACCESS_TOKEN** @return string*///刷新access_token 100分鐘刷新一次,服務器啟動的時候刷新一次(access_token有效期是120分鐘,我設置的是每100分鐘刷新一次)@Scheduled(initialDelay = 1000, fixedDelay = 100*60*1000)public String getAccessToken() {Map<String,String> resultMap=new HashMap<>();//獲取access_tokenString url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}";//設置參數Map<String, Object> map = new HashMap<>();map.put("appid", WX_APPID);map.put("secret", WX_APPSECRET);//發送get請求String accessTokenResult = ((Map<String, String>) JSON.parse(httpUtil.GETclient(url, map))).get("access_token");tokenAll = accessTokenResult;return accessTokenResult;}/*** 獲取jsapiTicket** @return map*/@Scheduled(initialDelay = 1000, fixedDelay = 100*60*1000)public String getJsapiTicket() {Map<String,Object> map=new HashMap<>();String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={ACCESS_TOKEN}&type=jsapi";//設置參數map.put("ACCESS_TOKEN", getAccessToken());//發送get請求String ticket = ((Map<String, String>) JSON.parse(httpUtil.GETclient(url, map))).get("ticket");ticketAll = ticket;return ticket;}/*** 生成wx.config** @return map*/public Map<String,String> wxConfig(String url) {String jsapi_ticket = ticketAll;// 注意 URL 一定要動態獲取,不能 hardcodeMap<String, String> ret = sign(jsapi_ticket, url);for (Map.Entry entry : ret.entrySet()) {System.out.println(entry.getKey() + ", " + entry.getValue());}return ret;}/*** 簽名* @return*/public Map<String, String> sign(String jsapi_ticket, String url){Map<String,String> ret = new HashMap<String, String>();String nonce_str = create_nonce_str();String timestamp = create_timestamp();String stringAppend;String signature = "";//注意這里參數名必須全部小寫,且必須有序stringAppend = "jsapi_ticket=" + jsapi_ticket +"&noncestr=" + nonce_str +"×tamp=" + timestamp +"&url=" + url;System.out.println(stringAppend);try{MessageDigest crypt = MessageDigest.getInstance("SHA-1");crypt.reset();crypt.update(stringAppend.getBytes("UTF-8"));signature = byteToHex(crypt.digest());}catch (NoSuchAlgorithmException e){e.printStackTrace();}catch (UnsupportedEncodingException e){e.printStackTrace();}ret.put("url", url);ret.put("jsapi_ticket", jsapi_ticket);ret.put("nonceStr", nonce_str);ret.put("timestamp", timestamp);ret.put("signature", signature);ret.put("appId", WX_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;}//Sha1加密public static String getSha1(String str){if(str==null||str.length()==0){return null;}char hexDigits[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};try {MessageDigest mdTemp = MessageDigest.getInstance("SHA1");mdTemp.update(str.getBytes("UTF-8"));byte[] md = mdTemp.digest();int j = md.length;char buf[] = new char[j*2];int k = 0;for (int i = 0; i < j; i++) {byte byte0 = md[i];buf[k++] = hexDigits[byte0 >>> 4 & 0xf];buf[k++] = hexDigits[byte0 & 0xf];}return new String(buf);} catch (Exception e) {return null;}}/*** 隨機字符串*/public String create_nonce_str() {return UUID.randomUUID().toString();}/*** 時間戳* @return*/private static String create_timestamp() {return Long.toString(System.currentTimeMillis() / 1000);} }網絡工具類.java
import org.springframework.http.*; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UrlPathHelper;import javax.servlet.http.HttpServletRequest; import java.io.UnsupportedEncodingException; import java.util.Map;/*** @program:gzdm* @author:wihenne* @creatTime:2021/05/06**/ @Component public class HttpUtil {//發起GET請求public String GETclient(String url, Map<String, Object> map) {RestTemplate restTemplate = new RestTemplate();HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);String result = restTemplate.getForObject(url, String.class, map);return result;}//發起POST請求,獲取圖片字節public byte[] getCodeImgBytes(String url,Map<String,Object> param){RestTemplate restTemplate = new RestTemplate();MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();HttpEntity requestEntity = new HttpEntity(param, headers);ResponseEntity<byte[]> entity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, byte[].class, new Object[0]);byte[] result = entity.getBody();return result;}/*** 獲得當前訪問的URL路徑* @param request* @return*/public static String getLocation(HttpServletRequest request) {UrlPathHelper helper = new UrlPathHelper();StringBuffer buff = request.getRequestURL();String uri = request.getRequestURI();String origUri = helper.getOriginatingRequestUri(request);buff.replace(buff.length() - uri.length(), buff.length(), origUri);String queryString = helper.getOriginatingQueryString(request);if (queryString != null) {buff.append("?").append(queryString);}try {return new String(buff.toString().getBytes(), "iso-8859-1");} catch (UnsupportedEncodingException e) {return buff.toString();}} }controler交互
/*** 獲取wechatConfig信息* @param url 訪問頁面的地址* @return*/@GetMapping("/getWechatConfig")public Map<String,String> getWechatConfig(String url){return wechatUtil.wxConfig(url);}js
//將url 存進緩存下 var url = "https://" + window.location.host; var urlCureent = encodeURIComponent(location.href); $.ajax({async:true,dataType:"json",type:"GET",url: url + "/verification/getWechatConfig?url=" + urlCureent,contentType: "application/json; charset=utf-8",success:function(data){wx.config({debug: 0,appId: data.appId,timestamp: data.timestamp,nonceStr: data.nonceStr,signature: data.signature,jsApiList: ["updateAppMessageShareData", "updateTimelineShareData","onMenuShareTimeline","onMenuShareAppMessage"]});wx.ready(function () { //需在用戶可能點擊分享按鈕前就先調用wx.updateAppMessageShareData({title: cardVo.cardName + "個人名片", // 分享標題desc: cardVo.cardNameEn + "'s card", // 分享描述link: location.href, // 分享鏈接,該鏈接域名或路徑必須與當前頁面對應的公眾號JS安全域名一致imgUrl: url + cardVo.cardImg, // 分享圖標success: function () {console.log("success");// 設置成功}});wx.updateTimelineShareData({title: cardVo.cardName + "個人名片", // 分享標題link: location.href, // 分享鏈接,該鏈接域名或路徑必須與當前頁面對應的公眾號JS安全域名一致imgUrl: url + cardVo.cardImg, // 分享圖標success: function () {console.log("success2");// 設置成功}});wx.onMenuShareTimeline({title: cardVo.cardName + "個人名片", // 分享標題desc: cardVo.cardNameEn + "'s card", // 分享描述link: location.href, // 分享鏈接,該鏈接域名或路徑必須與當前頁面對應的公眾號JS安全域名一致imgUrl: url + cardVo.cardImg, // 分享圖標});wx.onMenuShareAppMessage({title: cardVo.cardName + "個人名片", // 分享標題link: location.href, // 分享鏈接,該鏈接域名或路徑必須與當前頁面對應的公眾號JS安全域名一致imgUrl: url + cardVo.cardImg, // 分享圖標});});}});常見問題
獲取JSSDK權限成功但分享無效果
官方建議使用wx.updateAppMessageShareData、wx.updateTimelineShareData,但是我當時無效果,再加上舊版本接口wx.onMenuShareTimeline、wx.onMenuShareAppMessage就可以了。
本地調試無效果
本地調試需要ngrok等外網穿透再做白名單
微信頁面緩存
微信緩存機制,在js后面加個版本號即可。
<script type="text/javascript">var js = document.getElementById('cardJs');js.src = './gzdm/card.js?v='+ new Date().getTime();var css = document.getElementById('cardCss');css.href = './css/card.css?v='+ new Date().getTime(); </script>總結
以上是生活随笔為你收集整理的微信自定义分享链接内容,wx.updateAppMessageShareData、wx.updateTimelineShareData、wx.onMenuShareTimeline的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构与算法 c++描述 目录与源码
- 下一篇: 做SEO优化第九步:如何设置页面内容优化