002-JS-SDK開發使用,網頁獲取受權,掃一掃調用

1、概述

在申請響應的公衆號以後,實名認證或者企業認證以後,能夠進行對應開發javascript

2、開發步驟

2.一、開發前提【服務號】-域名設置

  登陸後臺以後→左側設置→公衆號設置→功能設置,設置好「JS接口安全域名","網頁受權域名"對應域名html

  

2.二、開發前提【服務號】-開發者ID設置

  登陸後臺以後→左側開發→基本配置→公衆號開發信息,設置好「開發者密碼(AppSecret)」java

2.三、查看接口信息

  登陸後臺以後→左側開發→接口權限→查看已開通的接口受權jquery

3、調用示例

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

3.一、示例1、微信網頁受權【地址

  1 第一步:用戶贊成受權,獲取code算法

  2 第二步:經過code換取網頁受權access_tokenc#

  3 第三步:刷新access_token(若是須要)api

  4 第四步:拉取用戶信息(需scope爲 snsapi_userinfo)

  5 附:檢驗受權憑證(access_token)是否有效

  1》總體流圖

    

  2》詳細參看地值

    微信網頁受權【地址

3.二、示例2、微信JS-SDK說明文檔【地址 或者地址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接口列表
});
View Code

      簽名算法地址,支持方法列表接口

    步驟四:經過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爲調用的接口名
    調用失敗時:其值爲具體錯誤信息

3.三、示例3、根據2.5.5 以掃一掃爲例:

  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();
    });
});
View Code

  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);
    }
}
View Code

    全局響應類

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;
    }
}
View Code

    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;
    }
}
View Code

    獲取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;
    }
View Code

    獲取基礎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;
    }
View Code

    獲取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;
    }
View Code

    注意 基礎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);
    }
View Code
相關文章
相關標籤/搜索