在申請響應的公衆號以後,實名認證或者企業認證以後,能夠進行對應開發javascript
登陸後臺以後→左側設置→公衆號設置→功能設置,設置好「JS接口安全域名","網頁受權域名"對應域名html
登陸後臺以後→左側開發→基本配置→公衆號開發信息,設置好「開發者密碼(AppSecret)」java
登陸後臺以後→左側開發→接口權限→查看已開通的接口受權jquery
JS-SDKweb
接口列表:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115 附錄2ajax
詳情頁:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1445241432redis
1 第一步:用戶贊成受權,獲取code算法
2 第二步:經過code換取網頁受權access_tokenc#
3 第三步:刷新access_token(若是須要)api
4 第四步:拉取用戶信息(需scope爲 snsapi_userinfo)
5 附:檢驗受權憑證(access_token)是否有效
1》總體流圖
2》詳細參看地值
微信網頁受權【地址】
JSSDK使用步驟【原文地址:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115】
步驟一:綁定域名
先登陸微信公衆平臺進入「公衆號設置」的「功能設置」裏填寫「JS接口安全域名」。
備註:登陸後可在「開發者中心」查看對應的接口權限。
步驟二:引入JS文件
在須要調用JS接口的頁面引入以下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.2.0.js
備註:支持使用 AMD/CMD 標準模塊加載方法加載
步驟三:經過config接口注入權限驗證配置
全部須要使用JS-SDK的頁面必須先注入配置信息,不然將沒法調用(同一個url僅需調用一次,對於變化url的SPA的web app可在每次url變化時進行調用,目前Android微信客戶端不支持pushState的H5新特性,因此使用pushState來實現web app的頁面會致使簽名失敗,此問題會在Android6.2中修復)。
wx.config({ debug: true, // 開啓調試模式,調用的全部api的返回值會在客戶端alert出來,若要查看傳入的參數,能夠在pc端打開,參數信息會經過log打出,僅在pc端時纔會打印。 appId: '', // 必填,公衆號的惟一標識 timestamp: , // 必填,生成簽名的時間戳 nonceStr: '', // 必填,生成簽名的隨機串 signature: '',// 必填,簽名 jsApiList: [] // 必填,須要使用的JS接口列表 });
步驟四:經過ready接口處理成功驗證
wx.ready(function(){ // config信息驗證後會執行ready方法,全部接口調用都必須在config接口得到結果以後,config是一個客戶端的異步操做,因此若是須要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。
//對於用戶觸發時才調用的接口,則能夠直接調用,不須要放在ready函數中。 });
步驟五:經過error接口處理失敗驗證
wx.error(function(res){ // config信息驗證失敗會執行error函數,如簽名過時致使驗證失敗,具體錯誤信息能夠打開config的debug模式查看,也能夠在返回的res參數中查看,對於SPA能夠在這裏更新簽名。 });
接口調用說明
全部接口經過wx對象(也可以使用jWeixin對象)來調用,參數是一個對象,除了每一個接口自己須要傳的參數以外,還有如下通用參數:
1.success:接口調用成功時執行的回調函數。
2.fail:接口調用失敗時執行的回調函數。
3.complete:接口調用完成時執行的回調函數,不管成功或失敗都會執行。
4.cancel:用戶點擊取消時的回調函數,僅部分有用戶取消操做的api纔會用到。
5.trigger: 監聽Menu中的按鈕點擊時觸發的方法,該方法僅支持Menu中的相關接口。
備註:不要嘗試在trigger中使用ajax異步請求修改本次分享的內容,由於客戶端分享操做是一個同步操做,這時候使用ajax的回包會尚未返回。
以上幾個函數都帶有一個參數,類型爲對象,其中除了每一個接口自己返回的數據以外,還有一個通用屬性errMsg,其值格式以下:
調用成功時:"xxx:ok" ,其中xxx爲調用的接口名
用戶取消時:"xxx:cancel",其中xxx爲調用的接口名
調用失敗時:其值爲具體錯誤信息
1》服務器端設置js域名
2》html頁面引用相關js
<script src="jquery.min.js" type="text/javascript"></script> <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
3》界面調用
<input type="button" value="掃一掃" id="btnScanCode" style="width: 80px"/>
4》相關JS代碼
jQuery(document).ready(function () { var getWechatSignUrl = "http://test.com/openapi/wechat/getJsApiSign'; // 獲取微信簽名 $.ajax({ url: getWechatSignUrl + "?url=" + encodeURIComponent(window.location.href.split('#')[0]), success: function (o) { console.log(o); if (o.code == 20000) { wxConfig(o.data.appId, o.data.timestamp, o.data.nonceStr, o.data.signature); } }, error: function (o) { alert("出錯了" + JSON.stringify(o)); } }); function wxConfig(_appId, _timestamp, _nonceStr, _signature) { var mm = '獲取數據:' + encodeURIComponent(window.location.href.split('#')[0]) + '\n' + _appId + '\n' + _timestamp + '\n' + _nonceStr + '\n' + _signature; console.log(mm); wx.config({ debug: true, // 開啓調試模式,調用的全部api的返回值會在客戶端alert出來,若要查看傳入的參數,能夠在pc端打開,參數信息會經過log打出,僅在pc端時纔會打印。 appId: _appId, // 必填,公衆號的惟一標識 timestamp: _timestamp, // 必填,生成簽名的時間戳 nonceStr: _nonceStr, // 必填,生成簽名的隨機串 signature: _signature,// 必填,簽名 jsApiList: ["scanQRCode"] // 必填,須要使用的JS接口列表 }); } function scanCode() { wx.scanQRCode({ needResult: 1, scanType: ["qrCode", "barCode"], success: function (res) { var result = res.resultStr; alert(result); }, fail: function (res) { console.log(res) alert(JSON.stringify(res)); } }); } //客戶端掃碼 $("#btnScanCode").click(function () { scanCode(); }); });
5》服務器端接口設計
全局響應枚舉類
public enum ResponseEnum { //帳戶類異常10000 USEREXCEPTION(10000, "帳戶異常"), SUCCESS(20000, "操做成功"), //參數類異常 30000 NOTFOUND(30000, "必輸參數爲空"), INPUTERROR(30001, "輸入參數格式錯誤"), DATEERROR(30002, "日期區間錯誤"), DATEOUTOF(30003, "日期區間超出範圍"), //系統類未知異常 99999 ERROR(99999, "系統異常"),; private int code; private String msg; ResponseEnum(int code, String msg) { this.code = code; this.msg = msg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } @Override public String toString() { return String.format("[%s:%s]", code, msg); } }
全局響應類
public class ResponseResult<T> { /** * 方法返回狀態碼 */ private int code; /** * 方法返回信息 */ private String msg; private T data; public ResponseResult(){ setResponseEnum(ResponseEnum.SUCCESS);} public ResponseResult(ResponseEnum enums){ setResponseEnum(enums); } public ResponseResult(ResponseEnum enums, T data){ setResponseEnum(ResponseEnum.SUCCESS); this.data = data; } public ResponseResult(T data){ this(ResponseEnum.SUCCESS,data); } public void setResponseEnum(ResponseEnum enums){ this.code = enums.getCode(); this.msg = enums.getMsg(); } public int getCode() { return code; } public String getMsg() { return msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
jsapi結果類
public class JsApiPageDomain { private String appId; // 必填,公衆號的惟一標識 private long timestamp; // 必填,生成簽名的時間戳 private String nonceStr; // 必填,生成簽名的隨機串 private String signature;// 必填,簽名 private String[] jsApiList;// 必填,須要使用的JS接口列表 public String getAppId() { return appId; } public void setAppId(String appId) { this.appId = appId; } public long getTimestamp() { return timestamp; } public void setTimestamp(long timestamp) { this.timestamp = timestamp; } public String getNonceStr() { return nonceStr; } public void setNonceStr(String nonceStr) { this.nonceStr = nonceStr; } public String getSignature() { return signature; } public void setSignature(String signature) { this.signature = signature; } public String[] getJsApiList() { return jsApiList; } public void setJsApiList(String[] jsApiList) { this.jsApiList = jsApiList; } }
獲取getJsApiSign
@RequestMapping(value = "/getJsApiSign", method = RequestMethod.GET) @ResponseBody public ResponseResult<JsApiPageDomain> getJsApiSign(String url) { ResponseResult<JsApiPageDomain> result = new ResponseResult<>(); JsApiPageDomain jsApiPageDomain = null; try { //返回頁面簽名等信息 //時間戳 long timeStamp = System.currentTimeMillis()/1000L; //字母數字隨機串 String nonceStr = RandomStringUtils.randomAlphanumeric(16); //獲取JsApITicket String jsApiTicket = weChatUtil.getJsApiTicket(); jsApiPageDomain = new JsApiPageDomain(); jsApiPageDomain.setAppId(globalConfig.getWeChatAppID()); jsApiPageDomain.setNonceStr(nonceStr); jsApiPageDomain.setTimestamp(timeStamp); jsApiPageDomain.setSignature(weChatUtil.signature(nonceStr, jsApiTicket, timeStamp, url)); jsApiPageDomain.setJsApiList(new String[]{"scanQRCode"}); result.setData(jsApiPageDomain); } catch (Exception e) { logger.error("getJsApiSign請求異常", e); } return result; }
獲取基礎access_token【參看地址】
/** * 獲取access_token * * @return */ public String getAccessToken() { //redis讀取 if (RedisUtil.exist(interfaceAccessTokenKey)) { String redis = RedisUtil.findValueFromRedis(interfaceAccessTokenKey); if (StringUtils.isNotBlank(redis)) { return redis; } } //執行調用 ResponseEntity<InterfaceAccessToken> entity = restTemplate.getForEntity(globalConfig.getWechatInterfaceAccessToken(), InterfaceAccessToken.class); if (entity != null && entity.getStatusCode() == HttpStatus.OK) { if (entity.getBody() != null&&StringUtils.isNotBlank(entity.getBody().getAccess_token())) { RedisUtil.saveToRedis(interfaceAccessTokenKey, entity.getBody().getAccess_token()); int tmp = entity.getBody().getExpires_in() - (60 * 5); RedisUtil.setExpire(interfaceAccessTokenKey, tmp>0?tmp:1, TimeUnit.SECONDS); return entity.getBody().getAccess_token(); } } return null; }
獲取jsapi_ticket【參看地址 附錄1】
/** * 獲取jsapi_ticket * * @return */ public String getJsApiTicket() { String token = getAccessToken(); if (StringUtils.isBlank(getAccessToken())) { return null; } //redis讀取 if (RedisUtil.exist(jsapiTicketKey)) { String redis = RedisUtil.findValueFromRedis(jsapiTicketKey); if (StringUtils.isNotBlank(redis)) { return redis; } } String apiTicket = MessageFormat.format(globalConfig.getWechatJsApiTicket(), token); //執行調用 ResponseEntity<JsApiTicket> entity = restTemplate.getForEntity(apiTicket, JsApiTicket.class); if (entity.getStatusCode() == HttpStatus.OK) { if (entity.getBody() != null&&StringUtils.isNotBlank(entity.getBody().getTicket())) { RedisUtil.saveToRedis(jsapiTicketKey, entity.getBody().getTicket()); //過時時間 Long ttl = RedisUtil.ttl(interfaceAccessTokenKey); if (ttl != null && (ttl.longValue() - 1 > 0)) { RedisUtil.setExpire(jsapiTicketKey, ttl.longValue() - 1, TimeUnit.SECONDS); } else { getJsApiTicket(); } return entity.getBody().getTicket(); } } return null; }
注意 基礎access_token 和 jsapi_ticket 使用爲redis緩存,同時具備相同的失效時間
簽名
public String signature(String noncestr, String jsapiTicket, long timestamp, String url) { TreeMap<String, String> map = new TreeMap<>();//之後擴展方便 map.put("noncestr", noncestr); map.put("jsapi_ticket", jsapiTicket); map.put("timestamp", String.valueOf(timestamp)); map.put("url", url); StringBuilder sb = new StringBuilder(); map.forEach((k, v) -> { sb.append(k + "=" + v + "&"); }); String msg = sb.toString().substring(0, sb.length() - 1); System.out.println("加密信息:" + msg); //簽名 MessageDigest m = null; try { m = MessageDigest.getInstance("SHA-1"); m.update(msg.getBytes("UTF8")); } catch (Exception e) { e.printStackTrace(); return null; } byte s[] = m.digest(); return Hex.encodeHexString(s); }