1. 程式人生 > >微信開放平臺之JSSDK分享

微信開放平臺之JSSDK分享

 微信分享開發流程:

步驟一:繫結域名

先登入微信公眾平臺進入“公眾號設定”的“功能設定”裡填寫“JS介面安全域名”。

 

 

步驟二:引入JS檔案

在需要呼叫JS介面的頁面引入如下JS檔案,(支援https):http://res.wx.qq.com/open/js/jweixin-1.4.0.js

如需進一步提升服務穩定性,當上述資源不可訪問時,可改訪問:http://res2.wx.qq.com/open/js/jweixin-1.4.0.js (支援https)。

備註:支援使用 AMD/CMD 標準模組載入方法載入

步驟三:通過config介面注入許可權驗證配置

   見微信官方文件 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115

//微信分享
//呼叫後臺介面獲取url簽名
      shareWXCTerminal: async function(){
        let shareParams = {
          appId: 'xxxxxxxxxxxxxxxxxx',
          url: encodeURIComponent(location.href.split('#')[0])
        }
        const res= await http.post("/weshare/getwxfxSign/ashx", shareParams)
        if(res.data.code==200&&res.data.data!=''){
          var getMsg=res.data.data
          wx.config({
            //注意駝峰命名
            debug: false,  //生產環境需要關閉debug模式
            appId: getMsg.appId, //appId通過微信服務號後臺檢視
            timestamp: getMsg.timestamp, //生成簽名的時間戳
            nonceStr: getMsg.nonceStr, //生成簽名的隨機字串
            signature: getMsg.signature, //簽名
            jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ','onMenuShareQZone']
          });
          wx.ready(function() {
            wx.onMenuShareTimeline({
              title:'wenjuan', // 分享標題
              link: location.href.split('#')[0]+'#'+location.href.split('#')[1],// 分享連結
              imgUrl: 'http://scrm.taikang.com/d6e18c2231ec926ebae2cc95a55ec3c3(0c95d4edab6d4f5397931f936c731b11).jpg', // 分享圖示
              success:function(res){

              }
            }),
            wx.onMenuShareAppMessage({
              title:'標題', // 分享標題
              desc: 'mianshuu', //分享描述
              link: location.href.split('#')[0]+'#'+location.href.split('#')[1],// 分享連結
              imgUrl: 'http://scrm.taikang.com/d6e18c2231ec926ebae2cc95a55ec3c3(0c95d4edab6d4f5397931f936c731b11).jpg', // 分享圖示
              success:function(res){

              }
            }),
              wx.onMenuShareQZone({
                title:'標題', // 分享標題
                desc: 'mianshuu', //分享描述
                link: location.href.split('#')[0]+'#'+location.href.split('#')[1],// 分享連結
                imgUrl: 'http://scrm.taikang.com/d6e18c2231ec926ebae2cc95a55ec3c3(0c95d4edab6d4f5397931f936c731b11).jpg', // 分享圖示
                success:function(res){

                }
              }),
              wx.onMenuShareQQ({
                title:'標題', // 分享標題
                desc: 'mianshuu', //分享描述
                link: location.href.split('#')[0]+'#'+location.href.split('#')[1],// 分享連結
                imgUrl: 'http://scrm.taikang.com/d6e18c2231ec926ebae2cc95a55ec3c3(0c95d4edab6d4f5397931f936c731b11).jpg', // 分享圖示
                success:function(res){

                }
              })
          });
        }

      },

後臺步驟

附錄1-JS-SDK使用許可權簽名演算法

jsapi_ticket

生成簽名之前必須先了解一下jsapi_ticket,jsapi_ticket是公眾號用於呼叫微信JS介面的臨時票據。正常情況下,jsapi_ticket的有效期為7200秒,通過access_token來獲取。由於獲取jsapi_ticket的api呼叫次數非常有限,頻繁重新整理jsapi_ticket會導致api呼叫受限,影響自身業務,開發者必須在自己的服務全域性快取jsapi_ticket 。

1.參考以下文件獲取access_token(有效期7200秒,開發者必須在自己的服務全域性快取access_token):../15/54ce45d8d30b6bf6758f68d2e95bc627.html

2.用第一步拿到的access_token 採用http GET方式請求獲得jsapi_ticket(有效期7200秒,開發者必須在自己的服務全域性快取jsapi_ticket):https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi

成功返回如下JSON:

{
"errcode":0,
"errmsg":"ok",
"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
"expires_in":7200
}

獲得jsapi_ticket之後,就可以生成JS-SDK許可權驗證的簽名了。

簽名演算法

簽名生成規則如下:參與簽名的欄位包括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.出於安全考慮,開發者必須在伺服器端實現簽名的邏輯。

程式碼示例:

 @PostMapping("/getwxfxSign/ashx")
    public BaseBean getSignature(@RequestBody Map<String,String> param) throws UnsupportedEncodingException {
        String appId = param.get("appId");
        String url = param.get("url");
        //前端請求中的 url 是 encodeURIComponent(location.href.split('#')[0])處理過的 
        //後端相應的要 解碼
        String decodeUrl = URLDecoder.decode(url, "UTF-8");
        SigVO sign = this.sign(appId, decodeUrl);
        return BaseBean.success(sign);
    }

    private SigVO sign(String appid, String url) {
        //32位隨機字串
        String nonceStr = WeShareUtils.CreateNoncestr();
        //時間戳
        String timeStamp = WeShareUtils.GetTimeStamp();
        //簽名
        String signature = StringUtils.EMPTY;
        //獲取jsapi_ticket
        Map<String, String> resultMap = new HashMap<>();
        getJsapiTicketCatch(appid,resultMap);
        String jsapiTicket = resultMap.get("tick");
        if (StringUtils.isBlank(jsapiTicket)) {
            return null;
        }
        //注意 引數欄位全為小寫
        String s1 = "jsapi_ticket=" + jsapiTicket + "&noncestr="
                + nonceStr + "&timestamp=" + timeStamp + "&url=" + url;
        try {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(s1.getBytes("UTF-8"));
            signature = WeShareUtils.byteToHex(crypt.digest());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return SigVO.builder()
                .appId(appid)
                .timestamp(timeStamp)
                .nonceStr(nonceStr)
                .signature(signature)
                .build();
    }

    private void getJsapiTicketCatch(String appid, Map<String, String> resultMap) {
        long expr = 90 * 60 * 1000L;
        long ex = 5 * 1000L;
        String value = String.valueOf(System.currentTimeMillis() + ex);
        boolean lock = redisLock.lock(appid, value);
        //獲取鎖
        if (lock) {
            String tick = redisTemplate.opsForValue().get("zqs"+appid);
            if (StringUtils.isNotBlank(tick)) {
                resultMap.put("tick",tick);
            } else {
                //token 通過appid
                BigAuthorizationInfo authorInfoByAppidService = authorizedService.getAuthorInfoByAppidService(appid);
                if (authorInfoByAppidService == null
                        || StringUtils.isBlank(authorInfoByAppidService.getAuthorizer_access_token())) {
                    resultMap.put("tick",null);
                }else {
                    String token = authorInfoByAppidService.getAuthorizer_access_token();
                    String jsapiTicket = WeShareUtils.GetJsapiTicket(token);
                    if (StringUtils.isBlank(jsapiTicket)) {
                        resultMap.put("tick",null);
                    }else {
                        resultMap.put("tick",jsapiTicket);
                        redisTemplate.opsForValue().set("zqs"+appid, jsapiTicket, expr,TimeUnit.MILLISECONDS);
                    }
                }
            }
            //釋放鎖
            redisLock.unlock(appid, value);
        }

    }

 上述程式碼未實現定時重新整理憑證jsapiTicket  而是通過redis設定失效時間  如果redis中沒有需要的憑證

則主動呼叫獲取jsapiTicket 的介面 獲取後存入redis中   未避免併發導致 介面呼叫超出限制次數 所以加鎖處理

微信官方的驗證簽名是否正確的網頁工具地址:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign

工具類:

package com.taikang.controller.share.WeShareUtil;

import com.alibaba.fastjson.JSONObject;
import com.taikang.constant.WeChatContants;
import com.taikang.controller.share.TicketJson;
import com.taikang.utils.weChat.WeChatUtils;

import java.util.Formatter;
import java.util.Random;

public class WeShareUtils {
    public static String GetJsapiTicket(String token) {
        String getTickUrl = WeChatContants.GET_TICK_URL;
        String url = String.format(getTickUrl, token);
        String result = WeChatUtils.getUrl(url);
        TicketJson ticketJson = JSONObject.parseObject(result, TicketJson.class);
        int errcode = ticketJson.getErrcode();
        if (errcode==0){
            return ticketJson.getTicket();
        }
        return null;
    }


    public static String GetTimeStamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }

    public static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;

    }

    public static String CreateNoncestr() {
        String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        StringBuilder res = new StringBuilder();
        Random rd = new Random();
        for (int i = 0; i < 32; i++) {
            res.append(chars.charAt(rd.nextInt(chars.length() - 1)));
        }
        return res.toString();
    }
}