1. 程式人生 > >微信內部分享-H5頁面-JS+java程式碼

微信內部分享-H5頁面-JS+java程式碼

app內部分享到微信,QQ都正常

再從微信開啟,繼續分享給朋友或者朋友圈,就變成下面這個樣子了

一切變得那麼的。。。無助!!!

開始以為在H5頁面上新增一些東西即可,後來發現,完全不是自己想象的那個樣子。

這個東西,對於一個從未用過微信JS的碼農來說,或許要被帶坑裡去卡個幾天!!!

以下是本人小菜的一點點經驗拿來和各位分享,希望剛接觸到的少走一些彎路!

微信公眾賬號平臺有提供一個js-sdk開發文件,有一定的開發規範,對又到分享等違規行為也有嚴格要求。按著開發文件做,區別就是通過後臺驗證一下APPID與SECRET是否正確,是否授權等,噁心之處就在後臺驗證。

首先,和微信公眾號開發一樣,首先必須做好最前面5個步驟,第三部最重要!!!

才可以使用JS呼叫微信介面,呼叫之前的驗證,對一個微信小白來講,是一個很繁瑣的過程

我這邊就先上JS和JAVA程式碼吧,現在講公眾號配置估計都是一臉懵逼

第一步:

1.在需要呼叫JS介面的頁面引入如下JS檔案

<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>

需要分享的H5頁面加上以下程式碼

<%--------------------------------------- 微信分享開始 ---------------------%>
<script>
// 只有先這樣取值,再賦值給下面的title,desc,imgUrl,link,不然IOS蘋果端無法呼叫成功

    var shareTitle = '${shareBean.shareTitle}';
    var shareContent = '${shareBean.shareContent}';
    var shareImage = '${shareBean.shareImage}';
 // 分享連結必須用這個,別問為什麼,微信就是坑
    var shareUrl = location.href.split('#')[0].toString();

    var title = shareTitle;
    var desc = shareContent;
    var imgUrl = shareImage;	// 分享的圖片,最好是正方形,不是也沒關係,但是一定是http模式,即絕對路徑,而不是伺服器路勁
    var link = shareUrl;	// 該連結域名或路徑必須與當前頁面對應的公眾號JS安全域名一致-----這個特別重要,請看清楚,這裡的地址可以寫死,也可以動態獲取,但是一定不能帶有微信分享後的引數,不然分享也是失敗的    
</script>
<c:import url="../wechat_share.jsp"/>----這個頁面寫公共的JS方法,在下面
<%--------------------------------------- 微信分享結束 ----------------------%>
<script>
    // 當前頁面訪問路徑
    var url = location.href.split('#')[0].toString();
    var getUrl = "wechat/getsignature";	// ajax請求路徑

    $.get(getUrl,
        {"url": url}).done(function (data) {
        // 注意這裡的url,一定要這樣寫,也就是動態獲取,千萬不要寫死,不然也不會成功的。連結字首要和安全域名一致
//        console.log(data);
//        console.log(data.code);
        if (data.code == 1) {
            var wxInfo = data.wxInfo;
            if (wxInfo.signature != null) {
                wx.config({
                    debug: false, // 開啟除錯模式,呼叫的所有api的返回值會在客戶端alert出來,若要檢視傳入的引數,可以在pc端開啟,引數資訊會通過log打出,僅在pc端時才會列印。
                    appId: wxInfo.appId, // 必填,公眾號的唯一標識
                    timestamp: wxInfo.timestamp, // 必填,生成簽名的時間戳
                    nonceStr: wxInfo.nonceStr, // 必填,生成簽名的隨機串
                    signature: wxInfo.signature,// 必填,簽名,見附錄1
                    jsApiList: [
                        'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareQZone'
                    ] // 必填,需要使用的JS介面列表,所有JS介面列表見附錄2
                });
            }
        }
    }).fail(function (msg) {
//        console.log("error:" + msg);
    });

    // 分享給朋友、QQ、微博
    var shareData = {
        "imgUrl": imgUrl,
        "title": title,
        "desc": desc,
        'link': link,
        success: function() {
//            layer.msg("分享成功~", {});
        },
        cancel: function() {
//            alert("取消分享");
        }
    };

    // 分享到朋友圈
    var shareToTimeline = {
        "imgUrl": imgUrl,
        "title": title,
        'link': link,
        success: function() {
//            layer.msg("分享成功~", {});
        },
        cancel: function() {
//            alert("取消分享");
        }
    }

    wx.ready(function () {
        wx.onMenuShareTimeline(shareToTimeline);        // 分享到微信朋友圈
        wx.onMenuShareAppMessage(shareData);            // 分享給微信朋友
        wx.onMenuShareQQ(shareData);                    // 分享到QQ
        wx.onMenuShareQZone(shareData);                 // 分享到QQ空間
        wx.onMenuShareWeibo(shareData);                 // 分享到微博

        wx.error(function (res) {
            alert(res.errMsg);
        });
    });

</script>
<script>
    // 當前頁面訪問路徑
    var url = location.href.split('#')[0].toString();
    var getUrl = "wechat/getsignature";	// ajax請求路徑

    $.get(getUrl,
        {"url": url}).done(function (data) {
        // 注意這裡的url,一定要這樣寫,也就是動態獲取,千萬不要寫死,不然也不會成功的。連結字首要和安全域名一致
//        console.log(data);
//        console.log(data.code);
        if (data.code == 1) {
            var wxInfo = data.wxInfo;
            if (wxInfo.signature != null) {
                wx.config({
                    debug: false, // 開啟除錯模式,呼叫的所有api的返回值會在客戶端alert出來,若要檢視傳入的引數,可以在pc端開啟,引數資訊會通過log打出,僅在pc端時才會列印。
                    appId: wxInfo.appId, // 必填,公眾號的唯一標識
                    timestamp: wxInfo.timestamp, // 必填,生成簽名的時間戳
                    nonceStr: wxInfo.nonceStr, // 必填,生成簽名的隨機串
                    signature: wxInfo.signature,// 必填,簽名,見附錄1
                    jsApiList: [
                        'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareQZone'
                    ] // 必填,需要使用的JS介面列表,所有JS介面列表見附錄2
                });
            }
        }
    }).fail(function (msg) {
//        console.log("error:" + msg);
    });

    // 分享給朋友、QQ、微博
    var shareData = {
        "imgUrl": imgUrl,
        "title": title,
        "desc": desc,
        'link': link,
        success: function() {
//            layer.msg("分享成功~", {});
        },
        cancel: function() {
//            alert("取消分享");
        }
    };

    // 分享到朋友圈
    var shareToTimeline = {
        "imgUrl": imgUrl,
        "title": title,
        'link': link,
        success: function() {
//            layer.msg("分享成功~", {});
        },
        cancel: function() {
//            alert("取消分享");
        }
    }

    wx.ready(function () {
        wx.onMenuShareTimeline(shareToTimeline);        // 分享到微信朋友圈
        wx.onMenuShareAppMessage(shareData);            // 分享給微信朋友
        wx.onMenuShareQQ(shareData);                    // 分享到QQ
        wx.onMenuShareQZone(shareData);                 // 分享到QQ空間
        wx.onMenuShareWeibo(shareData);                 // 分享到微博

        wx.error(function (res) {
            alert(res.errMsg);
        });
    });

</script>

第二步:

先登入微信公眾號平臺

ajax請求後臺java介面

/**
 * 用途描述:微信內部點選分享Controller
 *
 * @author 
 * @version 1.0.0
 * @date 2018年2月13日 上午9:38:42
 */
@RestController
@RequestMapping(value = "/wechat")
public class WeChatShareController extends BaseController {
    @Autowired
    private WeChatShareService weChatShareService;

    /**
     * 獲取微信加密資訊
     *
     * @param request
     * @return
     */
    @RequestMapping(value = "/getsignature")
    public IData toTranscript(HttpServletRequest request) {
        IData jdata = new DataMap();	// 這是我們公司自己封裝的類,知道意思就行
        Locale locale = RequestContextUtils.getLocaleResolver(request).resolveLocale(request);

        try {
            // 獲取url
            String url = this.objToString("url", "", request);           
            Map<String, Object> wxInfo = weChatShareService
.queryWechatInfo(url);

            IData code = this.sendPromptMsg("1", "獲取成功"); // 查詢成功!
            jdata.putAll(code);
            jdata.put("wxInfo", wxInfo);
            return jdata;
        } catch (Exception e) {
            e.printStackTrace();
            return this.sendPromptMsg("1000", locale); // 未知錯誤
        }

    }
}


/**
 * 用途描述:微信內部點選分享Service
 *
 * @author 
 * @version 1.0.0
 * @date 2018年2月13日 上午9:38:42
 */
@Service
public class WeChatShareService {

    @Autowired
    private WechatShareTokenDao wechatShareTokenDao;

    @Autowired
    private WechatShareTokenDao reckonShareTokenDao;


    /**
     * 獲取微信加密資訊
     *
     * @param url
     * @return
     * @throws Exception
     */
    public Map<String, Object> queryWechatInfo(String url) throws Exception {
        Map<String, Object> wxInfo = new HashMap<>();

// 這裡的兩個加密資訊,是我通過一個服務程式,開啟執行緒,每隔30分鐘獲取一次,並存儲到資料庫中,因為微信公眾號獲取次數有限,而且兩個都是2小時失效
        String accessToken = "";
        String jsapiTicket = "";
        //1、獲取AccessToken和jsapiTicket,先從資料庫獲取最近一次儲存的。
        Map<String, Object> result = wechatShareTokenDao.queryWechatInfo();
        if (null != result && !result.isEmpty()) {
            accessToken = CommonUtil.objToString(result.get("wechat_access_token"), "");
            jsapiTicket = CommonUtil.objToString(result.get("wechat_jsapi_ticket"), "");
        }

        //3、時間戳和隨機字串
        long currentTimes = System.currentTimeMillis();  // 時間戳
        String noncestr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);//隨機字串
        String timestamp = String.valueOf(currentTimes / 1000);// 時間戳

        //5、將引數排序並拼接字串
        String params = "jsapi_ticket=" + jsapiTicket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url=" + url;

        //6、將字串進行sha1加密
        String signature = Constant.getSHA1(params);

        //7、微信appId
        String appId = WeChatUitl.getAppIdWx();

        wxInfo.put("appId", appId);
        wxInfo.put("accessToken", accessToken);
        wxInfo.put("jsapiTicket", jsapiTicket);
        wxInfo.put("timestamp", timestamp);
        wxInfo.put("nonceStr", noncestr);
        wxInfo.put("params", params);
        wxInfo.put("signature", signature);

        return wxInfo;
    }
}
    /**
     * SHA、SHA1加密
     *
     * @parameter: str:待加密字串
     * @return: 加密串
     **/
    public static String getSHA1(String str) {
        try {
            MessageDigest digest = java.security.MessageDigest
                    .getInstance("SHA-1"); //如果是SHA加密只需要將"SHA-1"改成"SHA"即可
            digest.update(str.getBytes());
            byte messageDigest[] = digest.digest();
            // Create Hex String
            StringBuffer hexStr = new StringBuffer();
            // 位元組陣列轉換為 十六進位制 數
            for (int i = 0; i < messageDigest.length; i++) {
                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexStr.append(0);
                }
                hexStr.append(shaHex);
            }
            return hexStr.toString();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }
/**
 * 用途描述: 微信開發獲取資訊---微信開放平臺
 *
 * @author 
 * @version 1.0.0
 * @date 2018-02-13
 */
public class WeChatUitl { 
// 微信appid---微信公眾平臺
    private static String appIdWx = "aaaaaaaaaaaaaaa"; 

// 微信AppSecret---微信公眾平臺
    private static String appSecretWx = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; 


    private static String keyWx = "ccccccccccccccccccccccccc"; // 微信key     應用簽名

    public static String getAppIdWx() {
        return appIdWx;
    }

        WeChatUitl.appIdWx = appIdWx;
    }

    public static String getAppSecretWx() {
        return appSecretWx;
    }

    public static void setAppSecretWx(String appSecretWx) {
        WeChatUitl.appSecretWx = appSecretWx;
    }

    public static String getKeyWx() {
        return keyWx;
    }

    public static void setKeyWx(String keyWx) {
        WeChatUitl.keyWx = keyWx;
    }

    /**
     * 獲取access_token
     *
     * @return
     */
    public static String getAccessToken() {
        String access_token = "";
        String grant_type = "client_credential";//獲取access_token填寫client_credential
        String AppId = appIdWx;//第三方使用者唯一憑證
        String secret = appSecretWx;//第三方使用者唯一憑證金鑰,即appsecret
        //這個url連結地址和引數皆不能變
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=" + grant_type + "&appid=" + AppId + "&secret=" + secret;

        try {
            URL urlGet = new URL(url);
            HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
            http.setRequestMethod("GET"); // 必須是get方式請求
            http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            http.setDoOutput(true);
            http.setDoInput(true);
            System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 連線超時30秒
            System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 讀取超時30秒
            http.connect();
            InputStream is = http.getInputStream();
            int size = is.available();
            byte[] jsonBytes = new byte[size];
            is.read(jsonBytes);
            String message = new String(jsonBytes, "UTF-8");
            JSONObject demoJson = JSONObject.fromObject(message);
            System.out.println("JSON字串:" + demoJson);
            access_token = demoJson.getString("access_token");
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return access_token;
    }


    /**
     * 獲取jsapi_ticket
     *
     * @param access_token
     * @return
     */
    public static String getTicket(String access_token) {
        String ticket = null;
        String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + access_token + "&type=jsapi";//這個url連結和引數不能變
        try {
            URL urlGet = new URL(url);
            HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
            http.setRequestMethod("GET"); // 必須是get方式請求
            http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            http.setDoOutput(true);
            http.setDoInput(true);
            System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 連線超時30秒
            System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 讀取超時30秒
            http.connect();
            InputStream is = http.getInputStream();
            int size = is.available();
            byte[] jsonBytes = new byte[size];
            is.read(jsonBytes);
            String message = new String(jsonBytes, "UTF-8");
            JSONObject demoJson = JSONObject.fromObject(message);
            System.out.println("JSON字串:" + demoJson);
            ticket = demoJson.getString("ticket");
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ticket;
    }


    public static void main(String[] args) {
        //1、獲取AccessToken
        String accessToken = getAccessToken();
//        String accessToken = "6_Exc9VRFdPMLeF-4gPaJJGmoo-BJUGzgSJcs3vkT_y4eXPiQzRf1vdMvlVXNE85sfYH9AtQcdd-zptyD5t5S98VXSwIapyMoYjBNfvH7A11GZOoWs2u6agFlLS9NMqzTgN1N5V16BZrL1BnV_WTIaAIAHET";

        //2、獲取Ticket
        String jsapi_ticket = getTicket(accessToken);

        //3、時間戳和隨機字串
        String noncestr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);//隨機字串
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);//時間戳

        System.out.println("accessToken:" + accessToken + "\njsapi_ticket:" + jsapi_ticket + "\n時間戳:" + timestamp + "\n隨機字串:" + noncestr);

        //4、獲取url
        String url = "http://www.luiyang.com/add.html";

        // 根據JSSDK上面的規則進行計算,這裡比較簡單,我就手動寫啦
//        String[] ArrTmp = {"jsapi_ticket","timestamp","nonce","url"};
//        Arrays.sort(ArrTmp);
//        StringBuffer sf = new StringBuffer();
//        for(int i=0;i<ArrTmp.length;i++){
//            sf.append(ArrTmp[i]);
//        }

        //5、將引數排序並拼接字串
        String str = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url=" + url;

        //6、將字串進行sha1加密
        String signature = Constant.getSHA1(str);
        System.out.println("引數:" + str + "\n簽名:" + signature);

    }
}

確認簽名演算法是否正確

可用此頁面工具進行校驗

-------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------

自己寫一個定時任務程式,去獲取AccessToken,jsapi_ticket ,並存儲資料庫,加上redis快取,每次更新、讀取快取就可以了。

這樣就避免使用者每次分享,都要去呼叫介面獲取簽名,畢竟次數有限!!!

推薦用 spring security,簡單,方便,實用

第三步:

接下來最重要的步驟:

1,最好先去申請一個線上域名(花生殼),用於測試,不然只有釋出到線上測試了

2,在公總號後臺新增IP白名單

本機IP獲取方式

本地測試的時候ip不是這這樣查詢,具體怎麼獲取,emmmmm 我忘了

反正到時候呼叫“獲取access_token”介面,返回結果。
如非白名單IP呼叫,將返回錯誤碼:40164,日誌會列印錯誤資訊,xxx.xx.xx.xx 的ip不安全,就是這個

在這裡也一起把線上伺服器的IP也一起設定進去,因為每次設定的時候都要找老闆的手機來掃微信二維碼,腦殼疼

注意,如果線上伺服器ip地址有變動,必須修改微信公眾號ip白名單配置

第四步:

配置公眾號業務域名和js安全域名

按照步驟下載檔案,將檔案複製到專案根目錄tomcat專案根目錄




我這裡設定的域名,跟安卓,ios商量好了,叫他們設定介面訪問的字首要包含專案名!!!
比如專案名是:tb_project
本地測試呼叫:localhost:8080/taobao_project/querylist
花生殼呼叫介面方式:xxx.xicp.io/querylist



到這裡,就完全到位了,over