玩玩微信公众号Java版之七:自定义微信分享

来源:互联网 发布:淘宝网造牙粉 编辑:程序博客网 时间:2024/06/06 02:13

前面已经学会了微信网页授权,现在微信网页的功能也可以开展起来啦!

首先,我们先来学习一下分享,如何在自己的页面获取分享接口及让小伙伴来分享呢?

今天的主人公: 微信 JS-SDK, 对应官方链接为:微信JS-SDK说明文档

 

经过分析,要使用微信SJ-SDK需要完成如下工作:

 

由以上分析,我们需要做服务器的注入验证,另外在需要分享的页面中引入js文件,这样就可以调用微信JS-SDK中的接口啦~

 

下面首先开始实现注入验证功能,主要分为如下几步:

第一步,获取access_token:

access_token是微信接口号开发的基本数据,建议存到数据库中保存。(第三篇中已实现,可参考)

第二步,获取jsapi_ticket:

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

其中参数为第一步中获取的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_ticket12     private String ticket = "";13 14     // 有效时间15     private int expires_in = 0;16 17     // 微信appid18     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     @Override87     public JSAPITicket clone() throws CloneNotSupportedException88     {89         // TODO Auto-generated method stub90         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 tJSAPITicket106      * @return107      */108     private boolean saveJSAPITicket(JSAPITicket tJSAPITicket)109     {110         PreparedStatement pst = null;111         Connection conn = null;112         try113         {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 block135             e.printStackTrace();136             return false;137         }138         catch (SQLException e)139         {140             // TODO Auto-generated catch block141             e.printStackTrace();142             return false;143         }144         catch (Exception e)145         {146             // TODO: handle exception147             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         try19         {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}',// 必填,签名,见附录121     jsApiList: ['onMenuShareTimeline','onMenuShareQQ','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录222 });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 //分享到QQ34 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

其中说明:

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

2、验证接口中请注意参数名称(这里粗心弄错了,导致验证失败),另外验证错误原因可参考官方错误说明:附录5-常见错误及解决方法.

3、对应的接口功能,最终实现在左上角的更多按钮,请参考页面:

4、最终实现效果如下(以分享到qq空间为例):

 

这里多出了【分享到手机QQ】和【分享到QQ空间】两个按钮,点击【分享到QQ空间】,可看到:

 

恭喜你,成功做出了自己的分享页面! 继续加油吧~

 

阅读全文
0 0