JSSDK曾經引爆前端以及后端的工程師,其魔性的力量毋庸置疑,在我們的技術眼里它的實現原理和根本是不能夠被改變的,這篇文章就不對其js的實現做任何評價和解說了(因為我也不是很懂,哈哈),這里要說的是它的config配置實現,參考文檔:http://mp.weixin.qq.com/wiki/11/74ad127cc054f6b80759c40f77ec03db.html !
微信JS-SDK是微信公眾平台面向網頁開發者提供的基於微信內的網頁開發工具包,通過使用微信JS-SDK,網頁開發者可借助微信高效地使用拍照、選圖、語音、位置等手機系統的能力,同時可以直接使用微信分享、掃一掃、卡券、支付等微信特有的能力,為微信用戶提供更優質的網頁體驗;本篇將面向網頁開發者介紹微信JS-SDK如何使用及相關注意事項!JSSDK使用步驟:
-
步驟一:在微信公眾平台綁定安全域名
-
步驟二:后端接口實現JS-SDK配置需要的參數
-
步驟三:頁面實現JS-SDk中config的注入配置,並實現對成功和失敗的處理
(一)在微信公眾平台綁定安全域名
先登錄微信公眾平台進入“公眾號設置”的“功能設置”里填寫“JS接口安全域名”(如下圖),如果需要使用支付類接口,需要確保支付目錄在支付的安全域名下,否則將無法完成支付!(注:登錄后可在“開發者中心”查看對應的接口權限),因為我用的是測試號,所以登錄以后界面就如下圖:
(二)后端接口實現JS-SDK配置需要的參數
1 wx.config({ 2 debug: true, // 開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會打印。 3 appId: '', // 必填,公眾號的唯一標識 4 timestamp: , // 必填,生成簽名的時間戳 5 nonceStr: '', // 必填,生成簽名的隨機串 6 signature: '',// 必填,簽名,見附錄1 7 jsApiList: [] // 必填,需要使用的JS接口列表 8 });
我們查看js-sdk的配置文檔和以上的代碼可以發現config的配置需要4個必不可少的參數appId、timestamp、nonceStr、signature,這里的signature就是我們生成的簽名!
生成簽名之前必須先了解一下jsapi_ticket,jsapi_ticket是公眾號用於調用微信JS接口的臨時票據。正常情況下,jsapi_ticket的有效期為7200秒,通過access_token來獲取。由於獲取jsapi_ticket的api調用次數非常有限,頻繁刷新jsapi_ticket會導致api調用受限,影響自身業務,開發者必須在自己的服務全局緩存jsapi_ticket ,所以這里我們將jsapi_ticket的獲取放到定時任務中,因為它和token的生命周期是一致的,所以在這里我們將他們放到一起。
①將原有的定時任務WeChatTask中獲取token的代碼做如下修改:
1 package com.gede.wechat.common; 2 import java.text.SimpleDateFormat; 3 import java.util.Date; 4 import java.util.HashMap; 5 import java.util.Map; 6 7 import com.gede.web.util.GlobalConstants; 8 import com.gede.wechat.util.HttpUtils; 9 10 import net.sf.json.JSONObject; 11 /** 12 * @author gede 13 * @version date:2019年5月26日 下午7:50:38 14 * @description : 15 */ 16 public class WeChatTask { 17 /** 18 * @Description: 任務執行體 19 * @param @throws Exception 20 */ 21 public void getToken_getTicket() throws Exception { 22 Map<String, String> params = new HashMap<String, String>(); 23 //獲取token執行體 24 params.put("grant_type", "client_credential"); 25 params.put("appid", GlobalConstants.getInterfaceUrl("appid")); 26 params.put("secret", GlobalConstants.getInterfaceUrl("AppSecret")); 27 String jstoken = HttpUtils.sendGet( 28 GlobalConstants.getInterfaceUrl("tokenUrl"), params); 29 30 String access_token = JSONObject.fromObject(jstoken).getString( 31 "access_token"); // 獲取到token並賦值保存 32 GlobalConstants.interfaceUrlProperties.put("access_token", access_token); 33 34 //獲取jsticket的執行體 35 params.clear(); 36 params.put("access_token", access_token); 37 params.put("type", "jsapi"); 38 String jsticket = HttpUtils.sendGet( 39 GlobalConstants.getInterfaceUrl("ticketUrl"), params); 40 String jsapi_ticket = JSONObject.fromObject(jsticket).getString( 41 "ticket"); 42 GlobalConstants.interfaceUrlProperties.put("jsapi_ticket", jsapi_ticket); // 獲取到js-SDK的ticket並賦值保存 43 44 System.out.println("jsapi_ticket================================================" + jsapi_ticket); 45 System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"token為=============================="+access_token); 46 } 47 }
然后我們根據【JS-SDK使用權限簽名算法】對參數進行簽名得到signature,這里的url必須采用前端傳遞到后端,因為每次的url會有所變化,
②所以我們自定義一個權限簽名算法JSSDK_Config類,如下:
1 package com.gede.wechat.common; 2 3 import java.security.MessageDigest; 4 import java.util.Formatter; 5 import java.util.HashMap; 6 import java.util.UUID; 7 import com.gede.web.util.GlobalConstants; 8 9 /** 10 * @author gede 11 * @version date:2019年5月30日 下午2:52:57 12 * @description : 13 */ 14 public class JSSDK_Config { 15 16 /** 17 * @Description: 前端jssdk頁面配置需要用到的配置參數 18 * @param @return hashmap {appid,timestamp,nonceStr,signature} 19 * @param @throws Exception 20 * @author gede 21 */ 22 public static HashMap<String, String> jsSDK_Sign(String url) throws Exception { 23 String nonce_str = create_nonce_str(); 24 String timestamp=GlobalConstants.getInterfaceUrl("timestamp"); 25 String jsapi_ticket=GlobalConstants.getInterfaceUrl("jsapi_ticket"); 26 // 注意這里參數名必須全部小寫,且必須有序 27 String string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str 28 + "×tamp=" + timestamp + "&url=" + url; 29 MessageDigest crypt = MessageDigest.getInstance("SHA-1"); 30 crypt.reset(); 31 crypt.update(string1.getBytes("UTF-8")); 32 String signature = byteToHex(crypt.digest()); 33 HashMap<String, String> jssdk=new HashMap<String, String>(); 34 jssdk.put("appId", GlobalConstants.getInterfaceUrl("appid")); 35 jssdk.put("timestamp", timestamp); 36 jssdk.put("nonceStr", nonce_str); 37 jssdk.put("signature", signature); 38 return jssdk; 39 40 } 41 42 private static String byteToHex(final byte[] hash) { 43 Formatter formatter = new Formatter(); 44 for (byte b : hash) { 45 formatter.format("%02x", b); 46 } 47 String result = formatter.toString(); 48 formatter.close(); 49 return result; 50 } 51 52 private static String create_nonce_str() { 53 return UUID.randomUUID().toString(); 54 } 55 56 }
③編寫使用JSSDK的controller類。新增JssdkController,代碼如下:
1 package com.gede.wechat.controller; 2 3 import java.util.Map; 4 import org.springframework.stereotype.Controller; 5 import org.springframework.web.bind.annotation.RequestMapping; 6 import org.springframework.web.bind.annotation.RequestMethod; 7 import org.springframework.web.bind.annotation.RequestParam; 8 import org.springframework.web.bind.annotation.ResponseBody; 9 10 11 /** 12 * @author gede 13 * @version date:2019年5月30日 下午3:03:31 14 * @description : 15 */ 16 @Controller 17 @RequestMapping("/jssdk") 18 public class JssdkController { 19 /** 20 * @Description: 前端獲取微信JSSDK的配置參數 21 * @param @param response 22 * @param @param request 23 * @param @param url 24 * @param @throws Exception 25 */ 26 @RequestMapping(value="config",method=RequestMethod.POST) 27 @ResponseBody 28 public Message JSSDK_config( 29 @RequestParam(value = "url", required = true) String url) { 30 try { 31 System.out.println(url); 32 Map<String, String> configMap = JSSDK_Config.jsSDK_Sign(url); 33 return Message.success(configMap); 34 } catch (Exception e) { 35 System.out.println("error happened!!!"); 36 return Message.error(); 37 } 38 39 } 40 }
到這里我們后端對jssdk的簽名參數的封裝就基本完成了,其中用到了我們自己寫的Message類,代碼如下:
1 package com.gede; 2 3 import com.gede.Code; 4 5 /** 6 * @author gede 7 * @version date:2019年5月30日 下午3:09:35 8 * @description :用於消息回復 9 */ 10 public class Message { 11 private int code; 12 private String msg; 13 private Object data; 14 public Message() { 15 16 } 17 18 public int getCode() { 19 return code; 20 } 21 22 public void setCode(int code) { 23 this.code = code; 24 } 25 26 public String getMsg() { 27 return msg; 28 } 29 30 public void setMsg(String msg) { 31 this.msg = msg; 32 } 33 34 public Object getData() { 35 return data; 36 } 37 38 public void setData(Object data) { 39 this.data = data; 40 } 41 42 public Message(int code,String msg) { 43 this.code=code; 44 this.msg=msg; 45 } 46 47 public Message(int code,String msg,Object data) { 48 this.code=code; 49 this.msg=msg; 50 this.data=data; 51 } 52 /** 53 * 返回成功消息 54 * @param content 內容 55 * @return 成功消息 56 */ 57 public static Message success(String content, Object data) { 58 return new Message(Code.SUCCESS, content, data); 59 } 60 /** 61 * 返回成功消息 62 * @param content 內容 63 * @return 成功消息 64 */ 65 public static Message success(String content) { 66 return new Message(Code.SUCCESS, content); 67 } 68 /** 69 * 返回成功消息 70 * @param content 內容 71 * @return 成功消息 72 */ 73 public static Message success(Object data) { 74 System.out.println(Code.SUCCESS); 75 return new Message(Code.SUCCESS, "操作成功",data); 76 } 77 /** 78 * 返回成功消息 79 * @param content 內容 80 * @return 成功消息 81 */ 82 public static Message success() { 83 return new Message(Code.SUCCESS, "操作成功"); 84 } 85 /** 86 * 返回失敗消息 87 * @param content 內容 88 * @return 成功消息 89 */ 90 public static Message error(int code,String content, Object data) { 91 return new Message(code, content, data); 92 } 93 /** 94 * 返回失敗消息 95 * @param content 內容 96 * @return 成功消息 97 */ 98 public static Message error(String content, Object data) { 99 return new Message(Code.FAIL, content, data); 100 } 101 102 /** 103 * 返回失敗消息 104 * @param content 內容 105 * @return 成功消息 106 */ 107 public static Message error(String content) { 108 return new Message(Code.FAIL, content); 109 } 110 /** 111 * 返回失敗消息 112 * @param content 內容 113 * @return 成功消息 114 */ 115 public static Message error() { 116 return new Message(Code.FAIL, "操作失敗"); 117 } 118 119 }
code類代碼為:
1 package com.gede; 2 /** 3 * @author gede 4 * @version date:2019年5月30日 下午3:10:34 5 * @description :狀態碼 6 */ 7 public class Code { 8 public static final int SUCCESS = 200; //成功 9 public static final int PARAMATER = 400; //參數錯誤 10 public static final int FAIL =500; //系統異常 11 }
到這里我們就開始寫前端界面,來響應我們的方法。
(三)頁面實現JS-SDk中config的注入配置,並實現對成功和失敗的處理
這里在web目錄下新建jssdkconfig.jsp,在jsp頁面用ajax方式獲取並進行配置,並開啟debug模式,打開之后就可以看到配置是否成功的提示,簡單代碼如下:
1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 <!DOCTYPE html > 4 <html> 5 <head> 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 7 <meta name="viewport" content="width=device-width" /> 8 <title>JSSDk配置</title> 9 <script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> 10 <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script> 11 <script type="text/javascript"> 12 function jssdk() { 13 $.ajax({ 14 url : "http://zqfbk.iok.la/wechat/wechatconfig/jssdk", 15 type : 'post', 16 dataType : 'json', 17 contentType : "http://zqfbk.iok.la/mychat/jssdk/config", 18 data : { 19 'url' : location.href.split('#')[0] 20 }, 21 success : function(data) { 22 wx.config({ 23 debug : true, 24 appId : data.data.appId, 25 timestamp : data.data.timestamp, 26 nonceStr : data.data.nonceStr, 27 signature : data.data.signature, 28 jsApiList : [ 'checkJsApi', 'onMenuShareTimeline', 29 'onMenuShareAppMessage', 'onMenuShareQQ', 30 'onMenuShareWeibo', 'hideMenuItems', 31 'showMenuItems', 'hideAllNonBaseMenuItem', 32 'showAllNonBaseMenuItem', 'translateVoice', 33 'startRecord', 'stopRecord', 'onRecordEnd', 34 'playVoice', 'pauseVoice', 'stopVoice', 35 'uploadVoice', 'downloadVoice', 'chooseImage', 36 'previewImage', 'uploadImage', 'downloadImage', 37 'getNetworkType', 'openLocation', 'getLocation', 38 'hideOptionMenu', 'showOptionMenu', 'closeWindow', 39 'scanQRCode', 'chooseWXPay', 40 'openProductSpecificView', 'addCard', 'chooseCard', 41 'openCard' ] 42 }); 43 } 44 }); 45 } 46 47 function isWeiXin5() { 48 var ua = window.navigator.userAgent.toLowerCase(); 49 var reg = /MicroMessenger\/[5-9]/i; 50 return reg.test(ua); 51 } 52 53 window.onload = function() { 54 // if (isWeiXin5() == false) { 55 // alert("您的微信版本低於5.0,無法使用微信支付功能,請先升級!"); 56 // } 57 jssdk(); 58 }; 59 </script> 60 </head> 61 <body> 62 <h1>nihao</h1> 63 </body> 64 </html>
(四)pc端進行測試
我的瀏覽器是火狐developer版的,其他的也都差不多,打開瀏覽器按F12,找到一個叫網絡的窗口,也有的瀏覽器叫network像我的就是network。
①運行項目后,打開瀏覽器輸入訪問地址http://zqfbk.iok.la/mychat/;點擊Jssdk這里需要設置一下超鏈接,因為懶得從視圖控制器返回了,超鏈接代碼:
<h1>Welcome to Mychat</h1> <a href="<c:url value="/userinfo" />">UserInfo</a> <a href="<c:url value="/jssdkconfig.jsp" />">Jssdk</a>
②在我么點擊Jssdk的時候,先按f12 打開network視圖窗口。,然后再點擊,效果圖如下:
③如果你看到你的狀態碼中沒有404,那你多半成功了,我們找對對應的文件名為config的那一行點擊一下,進行查看,此時后台也會做出成功提示,上圖:
如果提示是這樣,那么標識我們的配置是成功的,那么到這里微信jssdk的配置就基本完成了。