天天看點

微信實作分享轉發功能

根據開發文檔,首先看一下前端需要什麼資料:

var currentUrl  = window.location.href;
//微信分享功能實作
$.ajax({//調取接口傳回驗證碼
    type:"get",
    url:"http://域名/接口名",
    data:{'url':currentUrl},
    success:function(res){
        console.log(res)
        if(res.success==true){
            //分享連結
            var imgUrl = '圖檔位址';
            var lineLink = 'http://域名/檔案名/';
            var descContent = "分享展示内容";
            var shareTitle = '分享标題';
            wx.config({
                debug: false, // 開啟調試模式,調用的所有api的傳回值會在用戶端alert出來,若要檢視傳入的參數,可以在pc端打開,參數資訊會通過log打出,僅在pc端時才會列印。
                appId: 'wxdbcbd7962d016d0b', // 必填,公衆号的唯一辨別
                timestamp: res.data.timestamp, // 必填,生成簽名的時間戳
                nonceStr: res.data.nonceStr, // 必填,生成簽名的随機串
                signature: res.data.signature,// 必填,簽名,見附錄1
                jsApiList: [
                    'checkJsApi',
                    'onMenuShareAppMessage',
                    'onMenuShareTimeline',
                    'onMenuShareQQ'

                ] // 必填,需要使用的JS接口清單,所有JS接口清單見附錄2
            });
            wx.ready(function(){
                // config資訊驗證後會執行ready方法,所有接口調用都必須在config接口獲得結果之後,config是一個用戶端的異步操作,是以如果需要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確定正确執行。對于使用者觸發時才調用的接口,則可以直接調用,不需要放在ready函數中。
                wx.checkJsApi({
                    jsApiList: ['onMenuShareAppMessage','onMenuShareTimeline','onMenuShareQQ'], // 需要檢測的JS接口清單,所有JS接口清單見附錄2,
                    success: function(res) {
                        // console.log(res);
                        // 以鍵值對的形式傳回,可用的api值true,不可用為false
                        // 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
                    }
                });
                wx.onMenuShareAppMessage({
                    title: shareTitle, // 分享标題
                    desc: descContent, // 分享描述
                    link: lineLink, // 分享連結,該連結域名或路徑必須與目前頁面對應的公衆号JS安全域名一緻
                    imgUrl: imgUrl, // 分享圖示
                    type: '', // 分享類型,music、video或link,不填預設為link
                    dataUrl: '', // 如果type是music或video,則要提供資料連結,預設為空
                    success: function () {
                        // 使用者确認分享後執行的回調函數
                        //alert('success')
                    },
                    cancel: function () {
                        // 使用者取消分享後執行的回調函數
                        // alert('false');
                    }
                });
                wx.onMenuShareTimeline({
                    title: shareTitle, // 分享标題
                    link: lineLink, // 分享連結,該連結域名或路徑必須與目前頁面對應的公衆号JS安全域名一緻
                    imgUrl: imgUrl, // 分享圖示
                    success: function () {
                        // 使用者确認分享後執行的回調函數
                        // alert('success')
                    },
                    cancel: function () {
                        // 使用者取消分享後執行的回調函數
                        // alert('false');
                    }
                });
                wx.onMenuShareQQ({
                    title: shareTitle, // 分享标題
                    desc: descContent, // 分享描述
                    link: lineLink, // 分享連結
                    imgUrl: imgUrl, // 分享圖示
                    success: function () {
                        // 使用者确認分享後執行的回調函數
                    },
                    cancel: function () {
                        // 使用者取消分享後執行的回調函數
                    }
                });

            });
            wx.error(function(res){
                // config資訊驗證失敗會執行error函數,如簽名過期導緻驗證失敗,具體錯誤資訊可以打開config的debug模式檢視,也可以在傳回的res參數中檢視,對于SPA可以在這裡更新簽名。
                //alert(res);
                return;
            });
        }
        else {
           // alert(res.message)
            return
        }
    },
    error:function(){
        console.log("error");
    }
});
           

通過上面的js可以看到背景需要提供三個參數

一、timestamp

根據文檔得知,要想得到timestamp需要得到jsapi_ticket,而要想得到jsapi_ticket需要得到aaccess_token。

1、擷取access_token

access_token有效期為7200秒,重複擷取将導緻上次擷取的access_token失效,是以問題是如何定時擷取并且把得到的資料放在本地。

1)建立一個AccessToken實體類

public class AccessToken {
    private String  accessToken;
    private int expiresin;

    public String getAccessToken() {
        return accessToken;
    }

    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }

    public int getExpiresin() {
        return expiresin;
    }

    public void setExpiresin(int expiresin) {
        this.expiresin = expiresin;
    }
}
           

2)寫一個servlet定時擷取access_token,并配置在web.xml中

AccessTokenServlet:

@WebServlet(name = "accessTokenServlet")
public class AccessTokenServlet extends HttpServlet{

    private static final long serialVersionUID = L;
    private static Logger log = LoggerFactory.getLogger(WxCommonUtil.class);

    @Override
    public void init() throws ServletException {
        TokenThread.appId = getInitParameter("appid");  //擷取servlet初始參數appid和appsecret
        TokenThread.appSecret = getInitParameter("appsecret");

        log.info("weixin api appid:{}", TokenThread.appId);
        log.info("weixin api appsecret:{}", TokenThread.appSecret);

        if ("".equals(TokenThread.appId) || "".equals(TokenThread.appSecret)) {
            log.error("appid and appsecret configuration error, please check carefully.");
        } else {
            // 啟動定時擷取access_token的線程
            new Thread(new TokenThread()).start();
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}
           

web.xml配置

<servlet>
    <servlet-name>accessTokenServlet</servlet-name>
    <servlet-class>com.loan.servlet.AccessTokenServlet</servlet-class>
    <init-param>
      <param-name>appid</param-name>
      <param-value>你的appid</param-value>
    </init-param>
    <init-param>
      <param-name>appsecret</param-name>
      <param-value>你的appsecret</param-value>
    </init-param>
    <load-on-startup>0</load-on-startup>
  </servlet>
           

3)TokenThread類

public class TokenThread implements Runnable {

    private static Logger log = LoggerFactory.getLogger(TokenThread.class);

    public static String appId = "";
    public static String appSecret= "";
    public static AccessToken accessToken = null;

    @Override
    public void run() {
        while (true) {
            try {
                accessToken = WxCommonUtil.getToken(appId,appSecret);
                if (null != accessToken) {
                    System.out.println(accessToken.getAccessToken());
                    Thread.sleep( * ); //擷取到access_token 休眠7000秒

                } else {
                    Thread.sleep( * ); //擷取的access_token為空 休眠3秒
                }
            } catch (Exception e) {
                System.out.println("發生異常:" + e.getMessage());
                e.printStackTrace();
                try {
                    Thread.sleep( * ); //發生異常休眠1秒
                } catch (Exception e1) {

                }
            }
        }
    }
}
           

4)WxCommonUtil工具類

public class WxCommonUtil {

    private static Logger log = LoggerFactory.getLogger(WxCommonUtil.class);
    public final static String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=你的appid&secret=你的secret";

    public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
        JSONObject jsonObject = null;
        try {
            // 建立SSLContext對象,并使用我們指定的信任管理器初始化
            TrustManager[] tm = { new MyX509TrustManager() };
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());
            // 從上述SSLContext對象中得到SSLSocketFactory對象
            SSLSocketFactory ssf = sslContext.getSocketFactory();

            URL url = new URL(requestUrl);
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            conn.setSSLSocketFactory(ssf);

            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            // 設定請求方式(GET/POST)
            conn.setRequestMethod(requestMethod);

            // 當outputStr不為null時向輸出流寫資料
            if (null != outputStr) {
                OutputStream outputStream = conn.getOutputStream();
                // 注意編碼格式
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }

            // 從輸入流讀取傳回内容
            InputStream inputStream = conn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }

            // 釋放資源
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            conn.disconnect();
            jsonObject = JSONObject.fromObject(buffer.toString());
        } catch (ConnectException ce) {
            log.error("連接配接逾時:{}", ce);
        } catch (Exception e) {
            log.error("https請求異常:{}", e);
        }
        return jsonObject;
    }
           

這樣accessToken可以通過TokenThread.accessToken.getAccessToken()擷取。

2、擷取jsapi_ticket

/**
     * 擷取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;
    }
           

3、擷取url

//這裡URL要從前端動态擷取,不能寫死
 String url=request.getParameter("url");
           

4、拼接字元串并進行sha1加密

String str = "jsapi_ticket="+jsapi_ticket+"&noncestr="+noncestr+"&timestamp="+timestamp+"&url="+url;
           
/**
     * sha1的加密算法
     *
     * @param decript
     * @return
     */
    public static String SHA1(String decript) {
        String signature="";
        try
        {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(decript.getBytes("UTF-8"));
            signature = byteToHex(crypt.digest());
            return signature;
        }
        catch (NoSuchAlgorithmException e)
        {
            e.printStackTrace();
        }
        catch (UnsupportedEncodingException e)
        {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 進制轉換
     * @param hash
     * @return
     */
    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;
    }
           

這樣一系列步驟之後就可以得到signature。

二、timestamp

String timestamp = String.valueOf(System.currentTimeMillis() / );
           

三、nonceStr

這樣就能得到三個參數啦。

最後是總的controller:

@RequestMapping(value = "/sign",method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"})
    @ResponseBody
    public ObjectOutput sign(HttpServletRequest request, HttpServletResponse response, HttpSession session){
        Hashtable hashtable = new Hashtable();
        //、擷取accessToken
        String accessToken= TokenThread.accessToken.getAccessToken();
        //、擷取Ticket
        String jsapi_ticket=WxCommonUtil.getTicket(accessToken);
        //、時間戳和随機字元串
        String noncestr = UUID.randomUUID().toString().replace("-", "").substring(, );
        String timestamp = String.valueOf(System.currentTimeMillis() / );
        //、擷取url
        String url=request.getParameter("url");
        //、講參數排序并拼接字元串
        String str = "jsapi_ticket="+jsapi_ticket+"&noncestr="+noncestr+"&timestamp="+timestamp+"&url="+url;
        //、将字元串進行sha1加密
        String signature =WxCommonUtil.SHA1(str);
        hashtable.put("signature",signature);
        hashtable.put("timestamp",timestamp);
        hashtable.put("nonceStr",noncestr);
        hashtable.put("url",url);
        output = Format.output(true, Constant.Success, hashtable, "");
        return output;
    }
           

繼續閱讀