玩玩微信公衆號Java版之七:自定義微信分享

前面已經學會了微信網頁受權,如今微信網頁的功能也能夠開展起來啦!javascript

首先,咱們先來學習一下分享,如何在本身的頁面獲取分享接口及讓小夥伴來分享呢?css

今天的主人公: 微信 JS-SDK, 對應官方連接爲:微信JS-SDK說明文檔html

 

通過分析,要使用微信SJ-SDK須要完成以下工做:前端

 

由以上分析,咱們須要作服務器的注入驗證,另外在須要分享的頁面中引入js文件,這樣就能夠調用微信JS-SDK中的接口啦~java

 

下面首先開始實現注入驗證功能,主要分爲以下幾步:算法

第一步,獲取access_token:sql

access_token是微信接口號開發的基本數據,建議存到數據庫中保存。(第三篇中已實現,可參考)數據庫

第二步,獲取jsapi_ticket:json

由官方文檔得知,只需Get方式調用接口:https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapiapi

其中參數爲第一步中獲取的access_token,調用方法已在工具類中。另jsapi_ticket有效時間爲2個小時,且每一個頁面均需用到接口驗證,所以可參考access_token,

將jsapi_ticket 存至數據庫,以便後續獲取。

獲取jsapi_ticket主方法可參考(請忽略註釋問題):

 1     /**
 2      * 獲取微信 js-api-ticket
 3      * @return
 4      */
 5     public JSAPITicket getJsApiTicket()
 6     {
 7 
 8         /*
 9          * 第一步,查詢數據庫中ticket是否已過時 未過時則直接獲取
10          */
11         if (updateJSAPITicket())
12         {
13             return mJSAPITicket;
14         }
15 
16         /* 第二步,獲取當前有效的access_token */
17         WeChatTokenService tWeChatTokenService = new WeChatTokenService();
18         // 此處獲取測試帳號的
19         String access_token = tWeChatTokenService.getToken(mAppid, mAppSecret).getToken();
20 
21         /* 第三步,則經過https調用獲取 jsapi_ticket */
22         if (!getJSApiTicketbyhttps(access_token))
23         {
24             System.out.println("獲取ticket失敗!");
25             return null;
26         }
27 
28         return mJSAPITicket;
29     }
View Code

 其中jsapi_ticket對應實體類爲:

 1 /**
 2  * 微信 JS-API-Ticket類
 3  * @author Damon
 4  */
 5 public class JSAPITicket implements Cloneable
 6 {
 7 
 8     // 微信 ticket流水號
 9     private String ticketid = "";
10 
11     // 微信jsapi_ticket
12     private String ticket = "";
13 
14     // 有效時間
15     private int expires_in = 0;
16 
17     // 微信appid
18     private String appid = "";
19 
20     // 申請用戶密鑰
21     private String appsecret = "";
22 
23     // 獲取時間
24     private String createtime = "";
25 
26     public String getTicketid()
27     {
28         return ticketid;
29     }
30 
31     public void setTicketid(String ticketid)
32     {
33         this.ticketid = ticketid;
34     }
35 
36     public String getTicket()
37     {
38         return ticket;
39     }
40 
41     public void setTicket(String ticket)
42     {
43         this.ticket = ticket;
44     }
45 
46     public int getExpires_in()
47     {
48         return expires_in;
49     }
50 
51     public void setExpires_in(int expires_in)
52     {
53         this.expires_in = expires_in;
54     }
55 
56     public String getAppid()
57     {
58         return appid;
59     }
60 
61     public void setAppid(String appid)
62     {
63         this.appid = appid;
64     }
65 
66     public String getCreatetime()
67     {
68         return createtime;
69     }
70 
71     public void setCreatetime(String createtime)
72     {
73         this.createtime = createtime;
74     }
75 
76     public String getAppsecret()
77     {
78         return appsecret;
79     }
80 
81     public void setAppsecret(String appsecret)
82     {
83         this.appsecret = appsecret;
84     }
85 
86     @Override
87     public JSAPITicket clone() throws CloneNotSupportedException
88     {
89         // TODO Auto-generated method stub
90         JSAPITicket cloneTicket = (JSAPITicket) super.clone();
91         return cloneTicket;
92     }
93 
94 }
View Code

對應表結構能夠參考:

對應的SQL腳本:

 1 drop table if exists WeChatJSAPITicket;
 2 
 3 /*==============================================================*/
 4 /* Table: WeChatJSAPITicket                                     */
 5 /*==============================================================*/
 6 create table WeChatJSAPITicket
 7 (
 8    ticketid             varchar(60) not null,
 9    ticket               varchar(300),
10    expires_in           int,
11    appid                varchar(60),
12    appsecret            varchar(60),
13    createtime           timestamp,
14    primary key (ticketid)
15 );
View Code

主方法調用的明細方法爲:

  1     /**
  2      * 獲取微信JS-API-Ticket信息
  3      * @return
  4      */
  5     private boolean updateJSAPITicket()
  6     {
  7         // 查詢數據庫數據,若是有則不用更新,無則須要更新
  8         Connection con = null;
  9         PreparedStatement stmt = null;
 10         ResultSet rs = null;
 11         // 判斷當前token是否在有效時間內
 12         String sql = " select * from wechatjsapiticket where appid ='" + mAppid + "' and appsecret ='" + mAppSecret
 13                 + "' and ( current_timestamp -createtime) <expires_in order by createTime desc limit 0,1";
 14         System.out.println(sql);
 15         try
 16         {
 17             // 建立數據庫連接
 18             con = DBConnPool.getConnection();
 19             // 建立處理器
 20             stmt = con.prepareStatement(sql);
 21             // 查詢Token,讀取1條記錄
 22             rs = stmt.executeQuery();
 23             if (rs.next())
 24             {
 25                 mJSAPITicket.setTicketid(rs.getString("ticketid"));
 26                 mJSAPITicket.setTicket(rs.getString("ticket"));
 27                 mJSAPITicket.setExpires_in(rs.getInt("expires_in"));
 28                 mJSAPITicket.setAppid(rs.getString("appid"));
 29                 mJSAPITicket.setAppsecret(rs.getString("appsecret"));
 30             }
 31             else
 32             {
 33                 System.out.println("未查詢到對應ticket");
 34                 return false;
 35             }
 36         }
 37         catch (Exception e)
 38         {
 39             // TODO: handle exception
 40             return false;
 41         }
 42 
 43 
 44         return true;
 45     }
 46 
 47 
 48     /**
 49      * 調用請求獲取ticket
 50      * @param access_token
 51      * @return
 52      */
 53     private boolean getJSApiTicketbyhttps(String access_token)
 54     {
 55 
 56         String current_time = new Date().getTime() + "";
 57 
 58         try
 59         {
 60             // 請求地址
 61             String path = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
 62             path = path.replace("ACCESS_TOKEN", access_token);
 63             String strResp = WeChatUtil.doHttpsGet(path, "");
 64             System.out.println(strResp);
 65 
 66             // 解析獲取的token信息
 67             Map<String, Object> tMap = WeChatUtil.jsonToMap(strResp);
 68 
 69             System.out.println(tMap.toString());
 70 
 71             mJSAPITicket.setTicketid(WeChatUtil.getMaxJSAPITicketID());
 72             mJSAPITicket.setTicket((String) tMap.get("ticket"));
 73             mJSAPITicket.setExpires_in(Integer.parseInt((String) tMap.get("expires_in")));
 74             mJSAPITicket.setAppid(mAppid);
 75             mJSAPITicket.setAppsecret(mAppSecret);
 76             mJSAPITicket.setCreatetime(current_time);
 77 
 78             System.out.println(mJSAPITicket.getTicket());
 79 
 80         }
 81         catch (HttpException e)
 82         {
 83             // TODO Auto-generated catch block
 84             e.printStackTrace();
 85             return false;
 86         }
 87         catch (IOException e)
 88         {
 89             // TODO Auto-generated catch block
 90             e.printStackTrace();
 91             return false;
 92         }
 93 
 94         // 存儲JS-API-Ticket至數據庫
 95         if (!saveJSAPITicket(mJSAPITicket))
 96         {
 97             return false;
 98         }
 99 
100         return true;
101     }
102 
103     /**
104      * 將獲取到的ticket信息存到數據庫
105      * @param tJSAPITicket
106      * @return
107      */
108     private boolean saveJSAPITicket(JSAPITicket tJSAPITicket)
109     {
110         PreparedStatement pst = null;
111         Connection conn = null;
112         try
113         {
114             JSAPITicket ticket = tJSAPITicket.clone();
115 
116             System.out.println(ticket.getTicketid() + ticket.getTicket());
117 
118             conn = DBConnPool.getConnection();
119             // 建立預處理器
120             pst = conn.prepareStatement("insert into wechatjsapiticket(ticketid, ticket, expires_in,appid, appsecret,createtime) values (?,?,?,?,?,?)");
121 
122             pst.setString(1, ticket.getTicketid());
123             pst.setString(2, ticket.getTicket());
124             pst.setInt(3, ticket.getExpires_in());
125             pst.setString(4, ticket.getAppid());
126             pst.setString(5, ticket.getAppsecret());
127             long now = new Date().getTime();
128             pst.setTimestamp(6, new java.sql.Timestamp(Long.parseLong(ticket.getCreatetime())));
129             pst.execute();
130 
131         }
132         catch (CloneNotSupportedException e)
133         {
134             // TODO Auto-generated catch block
135             e.printStackTrace();
136             return false;
137         }
138         catch (SQLException e)
139         {
140             // TODO Auto-generated catch block
141             e.printStackTrace();
142             return false;
143         }
144         catch (Exception e)
145         {
146             // TODO: handle exception
147             System.out.println("出現額外異常");
148             return false;
149         }
150 
151         return true;
152     }
View Code

 這樣就方便咱們獲取access_ticket啦!

第三步,實現數據簽名:

簽名生成規則以下:參與簽名的字段包括noncestr(隨機字符串), 有效的jsapi_ticket, timestamp(時間戳), url(當前網頁的URL,不包含#及其後面部分) 。

這裏須要用到四個參數,具體分析以下:

參數 說明
noncestr 隨機字符串,可用java.util.UUUID類實現
jsapi_ticket 調用前面方法獲取
timestamp 當前時間戳
url 傳入參數,每次由前端傳入

 

 

 

 

 

另外,參數加密算法爲SHA1加密,所以實現方法爲:

 1     /**
 2      * ticket數據簽名
 3      * @return
 4      */
 5     public WeChatJSAPISign getSignTicket(String requestUrl)
 6     {
 7         // 隨機字符串
 8         String noncestr = UUID.randomUUID().toString().replace("-", "");
 9         String jsapi_ticket = getJsApiTicket().getTicket();
10         long timestamp = new Date().getTime();
11 
12         String params = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + noncestr + "&timestamp=" + timestamp + "&url="
13                 + requestUrl;
14         String signature = "";
15 
16         System.out.println("params:" + params);
17 
18         try
19         {
20             MessageDigest crypt = MessageDigest.getInstance("SHA-1");
21             crypt.reset();
22             crypt.update(params.getBytes("UTF-8"));
23             signature = WeChatUtil.byteToHex(crypt.digest());
24         }
25         catch (NoSuchAlgorithmException e)
26         {
27             e.printStackTrace();
28         }
29         catch (UnsupportedEncodingException e)
30         {
31             e.printStackTrace();
32         }
33 
34         WeChatJSAPISign tChatJSAPISign = new WeChatJSAPISign();
35 
36         tChatJSAPISign.setAppId(mAppid);
37         tChatJSAPISign.setNoncestr(noncestr);
38         tChatJSAPISign.setTimestamp(timestamp);
39         tChatJSAPISign.setSignature(signature);
40 
41         return tChatJSAPISign;
42     }
View Code

返回定義的參數對象,定義以下:

 1 /**
 2  * // JS-API-Ticket 簽名類
 3  * @author Damon
 4  */
 5 public class WeChatJSAPISign
 6 {
 7 
 8     // JS-API-Ticket appid
 9     private String appId = "";
10 
11     // JS-API-Ticket 隨機字符串
12     private String noncestr = "";
13 
14     // JS-API-Ticket 時間
15     private long timestamp = 0;
16 
17     // JS-API-Ticket 簽名
18     private String signature = "";
19 
20     public String getAppId()
21     {
22         return appId;
23     }
24 
25     public void setAppId(String appId)
26     {
27         this.appId = appId;
28     }
29 
30     public String getNoncestr()
31     {
32         return noncestr;
33     }
34 
35     public void setNoncestr(String noncestr)
36     {
37         this.noncestr = noncestr;
38     }
39 
40     public long getTimestamp()
41     {
42         return timestamp;
43     }
44 
45     public void setTimestamp(long timestamp)
46     {
47         this.timestamp = timestamp;
48     }
49 
50     public String getSignature()
51     {
52         return signature;
53     }
54 
55     public void setSignature(String signature)
56     {
57         this.signature = signature;
58     }
59 
60 }
View Code

到這,注入驗證的服務器端功能就完成了。

下面也進行頁面的編寫和調用驗證。

第一步,先寫一個分享頁面(基本的html頁面便可),可參考(因爲個人工程默認編碼GBK,編碼請注意):

 1 <%@ page language="java" contentType="text/html; charset=GBK"   pageEncoding="GBK"%>
 2 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 3 <html>
 4 <head>
 5 <meta http-equiv="Content-Type" content="text/html; charset=GBK">
 6 <title>damon's share page</title>
 7 <%@include file="wechat_config.jsp" %>
 8 <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
 9 <link rel="stylesheet" href="./weui/weui.css"/>
10 
11 <!-- 
12 <script src="wechat_config.js"></script>
13  -->
14 <script type="text/javascript">
15 wx.config({
16     debug: false, // 開啓調試模式,調用的全部api的返回值會在客戶端alert出來,若要查看傳入的參數,能夠在pc端打開,參數信息會經過log打出,僅在pc端時纔會打印。
17     appId: '${appid}', // 必填,公衆號的惟一標識
18     timestamp: '${timestamp}', // 必填,生成簽名的時間戳
19     nonceStr: '${noncestr}', // 必填,生成簽名的隨機串
20     signature: '${signature}',// 必填,簽名,見附錄1
21     jsApiList: ['onMenuShareTimeline','onMenuShareQQ','onMenuShareQZone'] // 必填,須要使用的JS接口列表,全部JS接口列表見附錄2
22 });
23 
24 wx.ready(function(){
25     
26 // 分享到朋友圈
27 wx.onMenuShareTimeline({
28         title: '玩玩微信公衆號Java版之六:微信網頁受權', // 分享標題
29         link: 'http://www.cnblogs.com/cooldamon/p/7219400.html', // 分享連接,該連接域名或路徑必須與當前頁面對應的公衆號JS安全域名一致
30         imgUrl: 'http://damonhouse.iok.la/wechat/pic1.jpg' // 分享圖標
31     });
32     
33 //分享到QQ
34 wx.onMenuShareQQ({
35         title: '玩玩微信公衆號Java版之六:微信網頁受權', // 分享標題
36         desc: '分享測試', // 分享描述
37         link: 'http://www.cnblogs.com/cooldamon/p/7219400.html', // 分享連接,該連接域名或路徑必須與當前頁面對應的公衆號JS安全域名一致
38         imgUrl: 'http://damonhouse.iok.la/wechat/pic1.jpg' // 分享圖標
39     });
40     
41 //分享到QQ空間
42 wx.onMenuShareQZone({
43     title: '玩玩微信公衆號Java版之六:微信網頁受權', // 分享標題
44     desc: '分享QQ空間測試', // 分享描述
45     link: 'http://www.cnblogs.com/cooldamon/p/7219400.html', // 分享連接
46     imgUrl: 'http://damonhouse.iok.la/wechat/pic1.jpg' // 分享圖標
47 });
48     
49     
50 });
51 
52 wx.error(function(res){
53     // config信息驗證失敗會執行error函數,如簽名過時致使驗證失敗,具體錯誤信息能夠打開config的debug模式查看,也能夠在返回的res參數中查看,對於SPA能夠在這裏更新簽名。
54 });
55 
56 function shareMe()
57 {
58     //分享到QQ空間
59     wx.onMenuShareQZone({
60         title: '玩玩微信公衆號Java版之六:微信網頁受權', // 分享標題
61         desc: '分享QQ空間測試', // 分享描述
62         link: 'http://www.cnblogs.com/cooldamon/p/7219400.html', // 分享連接
63         imgUrl: 'http://damonhouse.iok.la/wechat/pic1.jpg' // 分享圖標
64     });
65     
66 }
67 
68 </script>
69 </head>
70 <body>
71 <button class="weui-btn weui-btn_plain-primary" onclick="shareMe();">歡迎分享</button>
72 </body>
73 </html>
View Code

另外這裏對微信接口調用寫了一個功能的方法,引入wechat_config.jsp

 1 <%@page import="com.wechat.pojo.WeChatJSAPISign"%>
 2 <%@page import="com.wechat.bl.WeChatJSAPIService"%>
 3 <%
 4 // 微信js-jdk 配置接口處理
 5 // 第一步,獲取參數
 6 %>
 7 
 8 <%
 9 
10 String url = request.getRequestURL().toString();
11 System.out.println(url);
12 WeChatJSAPIService tWeChatJSAPIService = new WeChatJSAPIService();
13 
14 WeChatJSAPISign tWeChatJSAPISign = tWeChatJSAPIService.getSignTicket(url);
15 System.out.println( tWeChatJSAPISign.getAppId());
16 System.out.println( tWeChatJSAPISign.getNoncestr());
17 System.out.println( tWeChatJSAPISign.getTimestamp());
18 System.out.println( tWeChatJSAPISign.getSignature());
19 
20 request.setAttribute("appid", tWeChatJSAPISign.getAppId());
21 request.setAttribute("noncestr", tWeChatJSAPISign.getNoncestr());
22 request.setAttribute("timestamp", tWeChatJSAPISign.getTimestamp());
23 request.setAttribute("signature", tWeChatJSAPISign.getSignature());
24 
25 %>
View Code

其中說明:

一、引入http://res.wx.qq.com/open/js/jweixin-1.2.0.j

二、驗證接口中請注意參數名稱(這裏粗心弄錯了,致使驗證失敗),另外驗證錯誤緣由可參考官方錯誤說明:附錄5-常見錯誤及解決方法.

三、對應的接口功能,最終實如今左上角的更多按鈕,請參考頁面:

四、最終實現效果以下(以分享到qq空間爲例):

 

這裏多出了【分享到手機QQ】和【分享到QQ空間】兩個按鈕,點擊【分享到QQ空間】,可看到:

 

恭喜你,成功作出了本身的分享頁面! 繼續加油吧~

相關文章
相關標籤/搜索