1. 程式人生 > >微信開發(一)--分享接口

微信開發(一)--分享接口

唯一標識 hash list 來源 andro 失敗 span 出錯 url

最近在做微信服務號開發,簡單總結一下,便於自己學習積累和分享給大家:

環境介紹:

Spring+ Spring MVC +Mybatis

開發語言:

JAVA

微信公眾平臺的開發中,微信只公布了一個基於Http協議的接口和加解密的算法sdk,在開發的過程中可以自己根據需求來配置數據,調用相關接口,也可以引用比較成熟的基於java語言的微信公眾號sdk。筆者引用的是目前比較流行的

weixin-java-tools

可以參考:點擊打開鏈接

項目中引入了weixin-java-mp 就可直接調用相關功能接口和完成jssdk認證

        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-mp</artifactId>
            <version>3.3.0</version>
        </dependency>
<dependency>
  <groupId>com.github.binarywang</
groupId> <artifactId>(不同模塊參考下文)</artifactId> <version>3.3.0</version> </dependency>
  • 各模塊的artifactId
    • 微信小程序:weixin-java-miniapp
    • 微信支付:weixin-java-pay
    • 微信開放平臺:weixin-java-open
    • 公眾號(包括訂閱號和服務號):weixin-java-mp
    • 企業號/企業微信:weixin-java-cp

https://gitee.com/binary/weixin-java-tools

MP_js_api
相關官方文檔

WxMpService提供了以下幾個方法:

public String getJsapiTicket() throws WxErrorException;

public String getJsapiTicket(boolean forceRefresh) throws WxErrorException;

public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException;

https://gitee.com/binary/weixin-java-tools/wikis/MP_js_api?sort_id=154599

分享接口:

1.後臺準備:
建一個公用控制類: WeXinJsSdkController ,通過ajax 訪問完成jssdk認證:

import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;

@Controller
@RequestMapping("jssdk")
public class WeXinJsSdkController {

    @Autowired
    private WxMpService wxMpService;

    @RequestMapping(value = "/config", method = RequestMethod.GET)
    @ResponseBody
    public WxJsapiSignature wxJsSdkConfig(HttpServletRequest request, String url) {
        try { // 直接調用wxMpServer 接口
            WxJsapiSignature wxJsapiSignature = wxMpService.createJsapiSignature(url);
            return wxJsapiSignature;
        } catch (WxErrorException e) {
            return null;
        }
    }

}

package me.chanjar.weixin.common.bean;

import java.beans.ConstructorProperties;
import java.io.Serializable;

public class WxJsapiSignature implements Serializable {
    private static final long serialVersionUID = -1116808193154384804L;
    private String appId;//參數名均為小寫字符
    private String nonceStr; //參數名均為小寫字符
    private long timestamp; //參數名均為小寫字符
    private String url;//包括‘http(s)://‘部分,以及‘?‘後面的GET參數部分,但不包括‘#‘hash後面的部分。
    private String signature; //參數名均為小寫字符

    public static WxJsapiSignature.WxJsapiSignatureBuilder builder() {
        return new WxJsapiSignature.WxJsapiSignatureBuilder();
    }

    public String getAppId() {
        return this.appId;
    }

    public String getNonceStr() {
        return this.nonceStr;
    }

    public long getTimestamp() {
        return this.timestamp;
    }

    public String getUrl() {
        return this.url;
    }

    public String getSignature() {
        return this.signature;
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    public void setNonceStr(String nonceStr) {
        this.nonceStr = nonceStr;
    }

    public void setTimestamp(long timestamp) {
        this.timestamp = timestamp;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public void setSignature(String signature) {
        this.signature = signature;
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof WxJsapiSignature)) {
            return false;
        } else {
            WxJsapiSignature other = (WxJsapiSignature)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                label63: {
                    Object this$appId = this.getAppId();
                    Object other$appId = other.getAppId();
                    if (this$appId == null) {
                        if (other$appId == null) {
                            break label63;
                        }
                    } else if (this$appId.equals(other$appId)) {
                        break label63;
                    }

                    return false;
                }

                Object this$nonceStr = this.getNonceStr();
                Object other$nonceStr = other.getNonceStr();
                if (this$nonceStr == null) {
                    if (other$nonceStr != null) {
                        return false;
                    }
                } else if (!this$nonceStr.equals(other$nonceStr)) {
                    return false;
                }

                if (this.getTimestamp() != other.getTimestamp()) {
                    return false;
                } else {
                    Object this$url = this.getUrl();
                    Object other$url = other.getUrl();
                    if (this$url == null) {
                        if (other$url != null) {
                            return false;
                        }
                    } else if (!this$url.equals(other$url)) {
                        return false;
                    }

                    Object this$signature = this.getSignature();
                    Object other$signature = other.getSignature();
                    if (this$signature == null) {
                        if (other$signature != null) {
                            return false;
                        }
                    } else if (!this$signature.equals(other$signature)) {
                        return false;
                    }

                    return true;
                }
            }
        }
    }

    protected boolean canEqual(Object other) {
        return other instanceof WxJsapiSignature;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $appId = this.getAppId();
        int result = result * 59 + ($appId == null ? 43 : $appId.hashCode());
        Object $nonceStr = this.getNonceStr();
        result = result * 59 + ($nonceStr == null ? 43 : $nonceStr.hashCode());
        long $timestamp = this.getTimestamp();
        result = result * 59 + (int)($timestamp >>> 32 ^ $timestamp);
        Object $url = this.getUrl();
        result = result * 59 + ($url == null ? 43 : $url.hashCode());
        Object $signature = this.getSignature();
        result = result * 59 + ($signature == null ? 43 : $signature.hashCode());
        return result;
    }

    public String toString() {
        return "WxJsapiSignature(appId=" + this.getAppId() + ", nonceStr=" + this.getNonceStr() + ", timestamp=" + this.getTimestamp() + ", url=" + this.getUrl() + ", signature=" + this.getSignature() + ")";
    }

    public WxJsapiSignature() {
    }

    @ConstructorProperties({"appId", "nonceStr", "timestamp", "url", "signature"})
    public WxJsapiSignature(String appId, String nonceStr, long timestamp, String url, String signature) {
        this.appId = appId;
        this.nonceStr = nonceStr;
        this.timestamp = timestamp;
        this.url = url;
        this.signature = signature;
    }

    public static class WxJsapiSignatureBuilder {
        private String appId;
        private String nonceStr;
        private long timestamp;
        private String url;
        private String signature;

        WxJsapiSignatureBuilder() {
        }

        public WxJsapiSignature.WxJsapiSignatureBuilder appId(String appId) {
            this.appId = appId;
            return this;
        }

        public WxJsapiSignature.WxJsapiSignatureBuilder nonceStr(String nonceStr) {
            this.nonceStr = nonceStr;
            return this;
        }

        public WxJsapiSignature.WxJsapiSignatureBuilder timestamp(long timestamp) {
            this.timestamp = timestamp;
            return this;
        }

        public WxJsapiSignature.WxJsapiSignatureBuilder url(String url) {
            this.url = url;
            return this;
        }

        public WxJsapiSignature.WxJsapiSignatureBuilder signature(String signature) {
            this.signature = signature;
            return this;
        }

        public WxJsapiSignature build() {
            return new WxJsapiSignature(this.appId, this.nonceStr, this.timestamp, this.url, this.signature);
        }

        public String toString() {
            return "WxJsapiSignature.WxJsapiSignatureBuilder(appId=" + this.appId + ", nonceStr=" + this.nonceStr + ", timestamp=" + this.timestamp + ", url=" + this.url + ", signature=" + this.signature + ")";
        }
    }
}




2.頁面初始化js 配置相關接口:

<%--通過config接口註入權限驗證配置--%> 
/* 初始化jssdk 分享功能 */
$.get("${basePath}/jssdk/config.do",{url:window.location.href},function(data,status){
if(status == "success"){
wx.config({
debug: false, // 開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會打印。
appId: data.appId, // 必填,公眾號的唯一標識
timestamp: data.timestamp, // 必填,生成簽名的時間戳
nonceStr: data.nonceStr, // 必填,生成簽名的隨機串
signature: data.signature,// 必填,簽名,見附錄1
jsApiList: [‘checkJsApi‘, ‘onMenuShareTimeline‘, ‘onMenuShareAppMessage‘, ‘onMenuShareQQ‘] // 必填,需要使用的JS接口列表,所有JS接口列表見附錄2

}); 


wx.ready(function(){
//    layer.msg("jssdk初始化成功");
// config信息驗證後會執行ready方法,所有接口調用都必須在config接口獲得結果之後,config是一個客戶端的異步操作,
//所以如果需要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。
//對於用戶觸發時才調用的接口,則可以直接調用,不需要放在ready函數中。

<%-- 分型內容 分享給朋友、朋友圈、 --%> 
var shareData = { 
title: ‘${requestScope.atc.trainsubject}‘, 
desc: ‘${description}‘, 
// link: ‘https://www.baidu.com‘, // 分享域名要和當前域名保持一致!!
imgUrl: ‘http://www.cyfd.cn/tektcrm/EventNoti/images/attendance.png‘, 
success: function (res) { 
// alert(‘已分享‘); 
}, 
cancel: function (res) { 
// alert(‘已取消‘); 
}, 
fail: function (res) { 
// alert(JSON.stringify(res)); 
} 
}; 

<%--分享給朋友接口--%> 
wx.onMenuShareAppMessage(shareData); 
<%--分享到朋友圈接口--%> 
wx.onMenuShareTimeline(shareData); 
<%--分享到QQ朋友接口--%> 
wx.onMenuShareQQ(shareData); 

});
wx.error(function(res){
// config信息驗證失敗會執行error函數,如簽名過期導致驗證失敗,具體錯誤信息可以打開config的debug模式查看,
//也可以在返回的res參數中查看,對於SPA可以在這裏更新簽名。
//    layer.msg(res);
});

}
},"json");


學習在於不斷地探索、思考和總結記錄,歡迎喜歡的朋友們在下方留言,與君共同進步!
---------------------
作者:Java小爬蟲
來源:CSDN
原文:https://blog.csdn.net/qq_36500554/article/details/80732334
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

簽名算法

簽名生成規則如下:參與簽名的字段包括noncestr(隨機字符串), 有效的jsapi_ticket, timestamp(時間戳), url(當前網頁的URL,不包含#及其後面部分) 。對所有待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)後,使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串string1。這裏需要註意的是所有參數名均為小寫字符。對string1作sha1加密,字段名和字段值都采用原始值,不進行URL 轉義。

即signature=sha1(string1)。 示例:

noncestr=Wm3WZYTPz0wzccnW
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
timestamp=1414587457
url=http://mp.weixin.qq.com?params=value

步驟1. 對所有待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)後,使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串string1:

jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW&timestamp=1414587457&url=http://mp.weixin.qq.com?params=value

步驟2. 對string1進行sha1簽名,得到signature:

0f9de62fce790f9a083d5c99e95740ceb90c27ed

註意事項

1.簽名用的noncestr和timestamp必須與wx.config中的nonceStr和timestamp相同。

2.簽名用的url必須是調用JS接口頁面的完整URL。

3.出於安全考慮,開發者必須在服務器端實現簽名的邏輯。

如出現invalid signature 等錯誤詳見附錄5常見錯誤及解決辦法。

附錄5-常見錯誤及解決方法
調用config 接口的時候傳入參數 debug: true 可以開啟debug模式,頁面會alert出錯誤信息。以下為常見錯誤及解決方法:

1.invalid url domain當前頁面所在域名與使用的appid沒有綁定,請確認正確填寫綁定的域名,僅支持80(http)和443(https)兩個端口,因此不需要填寫端口號(一個appid可以綁定三個有效域名,見 ]目錄1.1.1)。

2.invalid signature簽名錯誤。建議按如下順序檢查:

1.確認簽名算法正確,可用http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 頁面工具進行校驗。

2.確認config中nonceStr(js中駝峰標準大寫S), timestamp與用以簽名中的對應noncestr, timestamp一致。

3.確認url是頁面完整的url(請在當前頁面alert(location.href.split(‘#‘)[0])確認),包括‘http(s)://‘部分,以及‘?‘後面的GET參數部分,但不包括‘#‘hash後面的部分。

4.確認 config 中的 appid 與用來獲取 jsapi_ticket 的 appid 一致。

5.確保一定緩存access_token和jsapi_ticket。

6.確保你獲取用來簽名的url是動態獲取的,動態頁面可參見實例代碼中php的實現方式。如果是html的靜態頁面在前端通過ajax將url傳到後臺簽名,前端需要用js獲取當前頁面除去‘#‘hash部分的鏈接(可用location.href.split(‘#‘)[0]獲取,而且需要encodeURIComponent),因為頁面一旦分享,微信客戶端會在你的鏈接末尾加入其它參數,如果不是動態獲取當前鏈接,將導致分享後的頁面簽名失敗。

3.the permission value is offline verifying這個錯誤是因為config沒有正確執行,或者是調用的JSAPI沒有傳入config的jsApiList參數中。建議按如下順序檢查:

1.確認config正確通過。

2.如果是在頁面加載好時就調用了JSAPI,則必須寫在wx.ready的回調中。

3.確認config的jsApiList參數包含了這個JSAPI。

4.permission denied該公眾號沒有權限使用這個JSAPI,或者是調用的JSAPI沒有傳入config的jsApiList參數中(部分接口需要認證之後才能使用)。

5.function not exist當前客戶端版本不支持該接口,請升級到新版體驗。

6.為什麽6.0.1版本config:ok,但是6.0.2版本之後不ok(因為6.0.2版本之前沒有做權限驗證,所以config都是ok,但這並不意味著你config中的簽名是OK的,請在6.0.2檢驗是否生成正確的簽名以保證config在高版本中也ok。)

7.在iOS和Android都無法分享(請確認公眾號已經認證,只有認證的公眾號才具有分享相關接口權限,如果確實已經認證,則要檢查監聽接口是否在wx.ready回調函數中觸發)

8.服務上線之後無法獲取jsapi_ticket,自己測試時沒問題。(因為access_token和jsapi_ticket必須要在自己的服務器緩存,否則上線後會觸發頻率限制。請確保一定對token和ticket做緩存以減少2次服務器請求,不僅可以避免觸發頻率限制,還加快你們自己的服務速度。目前為了方便測試提供了1w的獲取量,超過閥值後,服務將不再可用,請確保在服務上線前一定全局緩存access_token和jsapi_ticket,兩者有效期均為7200秒,否則一旦上線觸發頻率限制,服務將不再可用)。

9.uploadImage怎麽傳多圖(目前只支持一次上傳一張,多張圖片需等前一張圖片上傳之後再調用該接口)

10.沒法對本地選擇的圖片進行預覽(chooseImage接口本身就支持預覽,不需要額外支持)

11.通過a鏈接(例如先通過微信授權登錄)跳轉到b鏈接,invalid signature簽名失敗(後臺生成簽名的鏈接為使用jssdk的當前鏈接,也就是跳轉後的b鏈接,請不要用微信登錄的授權鏈接進行簽名計算,後臺簽名的url一定是使用jssdk的當前頁面的完整url除去‘#‘部分)

12.出現config:fail錯誤(這是由於傳入的config參數不全導致,請確保傳入正確的appId、timestamp、nonceStr、signature和需要使用的jsApiList)

13.如何把jsapi上傳到微信的多媒體資源下載到自己的服務器(請參見文檔中uploadVoice和uploadImage接口的備註說明)

14.Android通過jssdk上傳到微信服務器,第三方再從微信下載到自己的服務器,會出現雜音(微信團隊已經修復此問題,目前後臺已優化上線)

15.綁定父級域名,是否其子域名也是可用的(是的,合法的子域名在綁定父域名之後是完全支持的)

16.在iOS微信6.1版本中,分享的圖片外鏈不顯示,只能顯示公眾號頁面內鏈的圖片或者微信服務器的圖片,已在6.2中修復

17.是否需要對低版本自己做兼容(jssdk都是兼容低版本的,不需要第三方自己額外做更多工作,但有的接口是6.0.2新引入的,只有新版才可調用)

18.該公眾號支付簽名無效,無法發起該筆交易(請確保你使用的jweixin.js是官方線上版本,不僅可以減少用戶流量,還有可能對某些bug進行修復,拷貝到第三方服務器中使用,官方將不對其出現的任何問題提供保障,具體支付簽名算法可參考 JSSDK微信支付一欄)

19.目前Android微信客戶端不支持pushState的H5新特性,所以使用pushState來實現web app的頁面會導致簽名失敗,此問題已在Android6.2中修復

20.uploadImage在chooseImage的回調中有時候Android會不執行,Android6.2會解決此問題,若需支持低版本可以把調用uploadImage放在setTimeout中延遲100ms解決

21.require subscribe錯誤說明你沒有訂閱該測試號,該錯誤僅測試號會出現

22.getLocation返回的坐標在openLocation有偏差,因為getLocation返回的是gps坐標,openLocation打開的騰訊地圖為火星坐標,需要第三方自己做轉換,6.2版本開始已經支持直接獲取火星坐標

23.查看公眾號(未添加): "menuItem:addContact"不顯示,目前僅有從公眾號傳播出去的鏈接才能顯示,來源必須是公眾號

24.ICP備案數據同步有一天延遲,所以請在第二日綁定

示例代碼:

http://demo.open.weixin.qq.com/jssdk/sample.zip

備註:鏈接中包含php、java、nodejs以及python的示例代碼供第三方參考,第三方切記要對獲取的accesstoken以及jsapi_ticket進行緩存以確保不會觸發頻率限制。

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115



微信開發(一)--分享接口