首先,我们先来学习一下分享,如何在自己的页面获取分享接口及让小伙伴来分享呢?
今天的主人公: 微信 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_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 + "×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 }
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: '', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
30 imgUrl: 'http://damonhouse.iok.la/wechat/pic1.jpg' // 分享图标
31 });
32
33 //分享到QQ
34 wx.onMenuShareQQ({
35 title: '玩玩微信公众号Java版之六:微信网页授权', // 分享标题
36 desc: '分享测试', // 分享描述
37 link: '', // 分享链接,该链接域名或路径必须与当前页面对应的公众号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: '', // 分享链接
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: '', // 分享链接
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空间】,可看到:
恭喜你,成功做出了自己的分享页面! 继续加油吧~