前面已經學會了微信網頁受權,如今微信網頁的功能也能夠開展起來啦!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 }
其中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 }
對應表結構能夠參考:
對應的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 );
主方法調用的明細方法爲:
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 }
這樣就方便咱們獲取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 + "×tamp=" + 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 }
返回定義的參數對象,定義以下:
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 }
到這,注入驗證的服務器端功能就完成了。
下面也進行頁面的編寫和調用驗證。
第一步,先寫一個分享頁面(基本的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>
另外這裏對微信接口調用寫了一個功能的方法,引入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 %>
其中說明:
一、引入http://res.wx.qq.com/open/js/jweixin-1.2.0.j
二、驗證接口中請注意參數名稱(這裏粗心弄錯了,致使驗證失敗),另外驗證錯誤緣由可參考官方錯誤說明:附錄5-常見錯誤及解決方法.
三、對應的接口功能,最終實如今左上角的更多按鈕,請參考頁面:
四、最終實現效果以下(以分享到qq空間爲例):
這裏多出了【分享到手機QQ】和【分享到QQ空間】兩個按鈕,點擊【分享到QQ空間】,可看到:
恭喜你,成功作出了本身的分享頁面! 繼續加油吧~