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

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

今天的主人公: 微信 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. }

其中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. return true;
  44. }
  45.  
  46. /**
  47. * 调用请求获取ticket
  48. * @param access_token
  49. * @return
  50. */
  51. private boolean getJSApiTicketbyhttps(String access_token)
  52. {
  53.  
  54. String current_time = new Date().getTime() + "";
  55.  
  56. try
  57. {
  58. // 请求地址
  59. String path = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
  60. path = path.replace("ACCESS_TOKEN", access_token);
  61. String strResp = WeChatUtil.doHttpsGet(path, "");
  62. System.out.println(strResp);
  63.  
  64. // 解析获取的token信息
  65. Map<String, Object> tMap = WeChatUtil.jsonToMap(strResp);
  66.  
  67. System.out.println(tMap.toString());
  68.  
  69. mJSAPITicket.setTicketid(WeChatUtil.getMaxJSAPITicketID());
  70. mJSAPITicket.setTicket((String) tMap.get("ticket"));
  71. mJSAPITicket.setExpires_in(Integer.parseInt((String) tMap.get("expires_in")));
  72. mJSAPITicket.setAppid(mAppid);
  73. mJSAPITicket.setAppsecret(mAppSecret);
  74. mJSAPITicket.setCreatetime(current_time);
  75.  
  76. System.out.println(mJSAPITicket.getTicket());
  77.  
  78. }
  79. catch (HttpException e)
  80. {
  81. // TODO Auto-generated catch block
  82. e.printStackTrace();
  83. return false;
  84. }
  85. catch (IOException e)
  86. {
  87. // TODO Auto-generated catch block
  88. e.printStackTrace();
  89. return false;
  90. }
  91.  
  92. // 存储JS-API-Ticket至数据库
  93. if (!saveJSAPITicket(mJSAPITicket))
  94. {
  95. return false;
  96. }
  97.  
  98. return true;
  99. }
  100.  
  101. /**
  102. * 将获取到的ticket信息存到数据库
  103. * @param tJSAPITicket
  104. * @return
  105. */
  106. private boolean saveJSAPITicket(JSAPITicket tJSAPITicket)
  107. {
  108. PreparedStatement pst = null;
  109. Connection conn = null;
  110. try
  111. {
  112. JSAPITicket ticket = tJSAPITicket.clone();
  113.  
  114. System.out.println(ticket.getTicketid() + ticket.getTicket());
  115.  
  116. conn = DBConnPool.getConnection();
  117. // 创建预处理器
  118. pst = conn.prepareStatement("insert into wechatjsapiticket(ticketid, ticket, expires_in,appid, appsecret,createtime) values (?,?,?,?,?,?)");
  119.  
  120. pst.setString(1, ticket.getTicketid());
  121. pst.setString(2, ticket.getTicket());
  122. pst.setInt(3, ticket.getExpires_in());
  123. pst.setString(4, ticket.getAppid());
  124. pst.setString(5, ticket.getAppsecret());
  125. long now = new Date().getTime();
  126. pst.setTimestamp(6, new java.sql.Timestamp(Long.parseLong(ticket.getCreatetime())));
  127. pst.execute();
  128.  
  129. }
  130. catch (CloneNotSupportedException e)
  131. {
  132. // TODO Auto-generated catch block
  133. e.printStackTrace();
  134. return false;
  135. }
  136. catch (SQLException e)
  137. {
  138. // TODO Auto-generated catch block
  139. e.printStackTrace();
  140. return false;
  141. }
  142. catch (Exception e)
  143. {
  144. // TODO: handle exception
  145. System.out.println("出现额外异常");
  146. return false;
  147. }
  148.  
  149. return true;
  150. }

这样就方便我们获取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. }

返回定义的参数对象,定义如下:

  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. wx.error(function(res){
  52. // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
  53. });
  54.  
  55. function shareMe()
  56. {
  57. //分享到QQ空间
  58. wx.onMenuShareQZone({
  59. title: '玩玩微信公众号Java版之六:微信网页授权', // 分享标题
  60. desc: '分享QQ空间测试', // 分享描述
  61. link: 'http://www.cnblogs.com/cooldamon/p/7219400.html', // 分享链接
  62. imgUrl: 'http://damonhouse.iok.la/wechat/pic1.jpg' // 分享图标
  63. });
  64.  
  65. }
  66.  
  67. </script>
  68. </head>
  69. <body>
  70. <button class="weui-btn weui-btn_plain-primary" onclick="shareMe();">欢迎分享</button>
  71. </body>
  72. </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. %>

其中说明:

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

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

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

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

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

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

玩玩微信公众号Java版之七:自定义微信分享的更多相关文章

  1. 玩玩微信公众号Java版之六:微信网页授权

    我们经常会访问一些网站,用微信登录的时候需要用到授权,那么微信网页授权是怎么一回事呢,一起来看看吧!   参考官方文档:https://mp.weixin.qq.com/wiki?t=resource ...

  2. 玩玩微信公众号Java版之四:自定义公众号菜单

    序: 微信公众号基本的菜单很难满足个性化及多功能的实现,那么微信能否实现自定菜单呢,具体的功能又如何去实现么?下面就来学习一下微信自定义公众号菜单吧! 自定义菜单接口可实现多种类型按钮,如下: 1.c ...

  3. 玩玩微信公众号Java版之一:配置微信公众平台服务器信息

    在进行微信公众平台开发前,前先做好准备工作,好了以后,我们可以开始啦!   第一.准备好服务端接口   定义一个http服务接口,主要分为如下几步:   1.创建一个servlet类,用来接收请求: ...

  4. 玩玩微信公众号Java版之准备

    微信自2013年流行起来,现在的发展已经超过了我们的想象,那么对应的公众平台,小程序等都是让人眼前一亮的东西,这里来学习一下微信工作号的对接,实现为Java,希望大家一起学习!   这里大概描述一下所 ...

  5. 玩玩微信公众号Java版之三:access_token及存储access_token

    微信官方参考文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183   基本说明: access_token是 ...

  6. 玩玩微信公众号Java版之五:获取关注用户信息

    在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的.对于不同公众号,同一用户的openid不同).公众号可通过本接口来根据Op ...

  7. 玩玩微信公众号Java版之二:接收、处理及返回微信消息

    前面已经配置了微信服务器,那么先开始最简单的接收微信消息吧~   可以用我们的微信号来直接进行测试,下面先看测试效果图:   这是最基本的文本消息的接收.处理及返回,来看看是怎么实现的吧!   首先可 ...

  8. JAVA微信公众号网页开发——生成自定义微信菜单(携带参数)

    官网接口地址:https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Creating_Custom-Defined_Menu.ht ...

  9. 微信公众号Java接入demo

    微信公众号Java接入demo 前不久买了一台服务,本来是用来当梯子用的,后来买了一个域名搭了一个博客网站,后来不怎么在上面写博客一直闲着,最近申请了一个微信公众号就想着弄点什么玩玩.周末没事就鼓捣了 ...

随机推荐

  1. superset在 centos 7安装运行

    参考:1.http://blog.csdn.net/u014729236/article/details/76302888?locationNum=2&fps=1 2.https://www. ...

  2. 项目管理利器maven学习笔记(一):maven介绍及环境搭建

    maven介绍 maven下载与环境搭建 http://maven.apache.org/download.cgi# 解压到指定位置,比如我解压到D盘 设置maven环境变量 添加一个变量名,变量值为 ...

  3. 自己实现HashMap

    一载体 HashMap是由数组组成,数组元素为哈希链. 数组 public class MyHashMap<K, V> { transient Node<K, V>[] tab ...

  4. jQuery取得radio的值 取select得值

    获取一组单选按钮对象: var obj_payPlatform = $('#wrap input[name="payMethod"]'); 获取被选中按钮的值 : var val_ ...

  5. Django form表单功能的引用(注册,复写form.clean方法 增加 验证密码功能)

    1. 在app下 新建 forms.py 定义表单内容,类型models from django import forms class RegisterForm(forms.Form): userna ...

  6. innodb 体系结构(后台进程)

    一.后台进程(innodb 1.0.x以前的) 1.master thread master thread具有最高的线程优先级别,其内部由多个循环(loop)组成:主循环(loop).后台循环(bac ...

  7. c++ vector push_back对象的时候存起来的是拷贝

    比如 class C1; vector<C1> vec;C1* p=new C1;vec v1;v1.push_back(&(*p));delete p; 这里,传进函数的是引用, ...

  8. Babel插件:@babel/plugin-transform-runtime

    一 概述 每个Babel编译后的脚本文件,都以导入的方式使用Babel的帮助函数,而不是每个文件都复制一份帮助函数的代码. 1 优点 (1)提高代码重用性,缩小编译后的代码体积. (2)防止污染全局作 ...

  9. swift UIview上添加视频播放

    1. /// 是否显示过广告 private lazy var isLoadAV = false /// 15秒宣传视频 private var play: AVPlayer? /// 宣传视频背景 ...

  10. 笔记之monkey自定义脚本

    自定义脚本的稳定性测试 常规MOnkey测试执行的是随机的事件流,但如果只是想让Monkey测试某个特定场景者时候就需要用到自定义脚本,Monkey支持执行用户自定义脚本的测试,用户之需要按照Monk ...