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


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

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

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

    其中说明:

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

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

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

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

     

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

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

  • 相关阅读:
    使用form插件 和ajax 结合使用 没有调用success的原因
    不使用插件的ajax 上传文件
    struts2 使用ajax进行图片上传
    Java输入输出流详解
    SSM框架整合(Spring+SpringMVC+MyBatis+Oracle)
    Java WEB开发环境搭建以及创建Maven Web项目
    java连接Oracle数据库
    java轻量级IOC框架Guice
    Java String字符串方法
    python入门
  • 原文地址:https://www.cnblogs.com/cooldamon/p/7240886.html
Copyright © 2020-2023  润新知