随笔-12  评论-6  文章-0  trackbacks-0
需求描述:公司通过APP产品分享出去的需求和简历是做了一个H5页面作为分享的链接,通过APP分享出去自然是没问题,也是第一次分享,之后通过微信打开H5页面后想再次分享出去时候就变成了一个链接了,而不是自己定制的卡片模式,初次分享后如下:
但是打开以后的H5页面再分享出去就变成这个样子了:

也就是说需要在H5页面做微信分享的相关工作,JS-SDK上场了,首先看看JS-SDK的官方说明文档:https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95
按照文档说明一步一步的做下去就可以出结果了,在这里详细说一下每一步如何操作以及如何避坑,重点在于如何避坑。
  1. 绑定域名
    1. 先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。
    2. 上面是说明文档的原话,这里我介绍一下在开发阶段如何测试。
      1. 首先你得有一个微信公众平台测试账号,总不能用公司的公众账号进行开发测试吧,当然你有自己的公众号是最好的,没有的话就快速的申请一个接口测试号吧
      2. 访问微信公众平台测试版系统:http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
      1. 主要是获取到测试号的appID和appsecret以及绑定J接口的安全域名:
      1. 绑定域名这块其实有蛮多坑的,就将我遇到的坑给大家说一下,希望遇到的人能够很好的解决:
        1. 域名不能加http://或者https://前缀,直接test.wxwx.com好了,否则回报invalid domain url无效的域
        2. 可以加端口号比如:test.wxwx.com:8888
        3. 也可以是一个ip地址比如:123.45.24.37
        4. 如何在本地进行测试?请下载一个代理服务器工具Fiddler:https://www.telerik.com/download/fiddler然后选择选项Tools->HOSTS
        1. 前面是你自己机器的Ip地址,后面是你自己定义的一个地址,然后在公众号里面添加wxwuwei.ceshiweixin.com:8888,Fiddler的端口是8888,然后保证你的机器和你的手机在同一个局域网下,进入手机设置你的网络连接如下图所示:
        1. 这个手机设置的ip一定要和你的PC的ip一样,这样前期环境准备工作就完成了,就可以开始编码部分了~
  1. 页面引入JS文件http://res.wx.qq.com/open/js/jweixin-1.0.0.js,这个就不多说了
  2. 通过config接口注入配置信息,微信需要去验证的, 如果验证通过了会执行wx.ready方法,这个JS-SDK文档有详细说明,这里主要说一下后台如何生成config中需要的配置信息:
    1 wx.config({
    2     debug: true// 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    3     appId: '', // 必填,公众号的唯一标识
    4     timestamp: , // 必填,生成签名的时间戳
    5     nonceStr: '', // 必填,生成签名的随机串
    6     signature: '',// 必填,签名,见附录1
    7     jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
    8 });
  1. appId我们已经获取到了,timestamp时间戳是到秒的,千万别到毫秒,可以new Date().getTime()/1000获取一个就可以了,nonceStr是咱们自己定义的一个串,可以是随机串也可以是一个固定的字符串,signature是根据当前的jsapi_ticket、nonceStr、timestamp、url(当前网页的url,不包含#及其后面部分)四个字段拼串(需要按照ASCII码从小到大排序进行拼串abcdefg的顺序)进行SHA1加密生成的,这些都是需要在服务器端实现。
  2. access_token的获取https://mp.weixin.qq.com/wiki/15/54ce45d8d30b6bf6758f68d2e95bc627.html
    1. 需要缓存,因为这个接口的调用是有次数限制的,可以放在redis中
  3. 卡券 api_ticket的获取:根据获取到的access_token来获取api_ticket
    1. 获取url:https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=wx_card
    2. 需要缓存,也是有次数限制的,可以放在redis中
  4. 常见错误及解决方法:见说明文档的附录5-常见错误及解决方法,其中的红字部分是你需要注意的坑,也是你解决微信多次分享的关键之处:确保你获取用来签名的url是动态获取的,动态页面可参见实例代码中php的实现方式。如果是html的静态页面在前端通过ajax将url传到后台签名,前端需要用js获取当前页面除去'#'hash部分的链接(可用location.href.split('#')[0]获取,而且需要encodeURIComponent),因为页面一旦分享,微信客户端会在你的链接末尾加入其它参数,如果不是动态获取当前链接,将导致分享后的页面签名失败
下面根据上面描述的步骤贴出部分代码(Java版的),仅供参考!
首先写一个WeixinUtils.java类:
  1 package com.freekeer.c.util;
  2 
  3 import java.security.MessageDigest;
  4 import java.util.Formatter;
  5 import java.util.HashMap;
  6 import java.util.Map;
  7 import java.util.Properties;
  8 
  9 import org.apache.commons.lang3.StringUtils;
 10 
 11 import com.alibaba.fastjson.JSONObject;
 12 
 13 public class WeiXinUtils {
 14 
 15     // 微信appId
 16     private static String APPID;
 17     // 微信公众号唯一密钥
 18     private static String APPSECRET;
 19     // 获取acc_token的接口
 20     private static String ACC_TOKEN_URL;
 21     // 获取jsapi_ticket url
 22     private static String JSAPI_TICKET_URL;
 23     // 生成签名的随机串
 24     private static String NONCE_STR;
 25 
 26     static {
 27         Properties prop = AppPropTools.getProperties("/weixin.properties");
 28         APPID = prop.getProperty("APPID");
 29         APPSECRET = prop.getProperty("APPSECRET");
 30         ACC_TOKEN_URL = prop.getProperty("ACC_TOKEN_URL"+ "&appid=" + APPID
 31                 + "&secret=" + APPSECRET;
 32         JSAPI_TICKET_URL = prop.getProperty("JSAPI_TICKET_URL");
 33         NONCE_STR = prop.getProperty("NONCE_STR");
 34     }
 35 
 36     // 私有构造方法
 37     private WeiXinUtils() {
 38 
 39     }
 40 
 41     /**
 42      * 获取微信acc_token
 43      * 
 44      * @return
 45      * @throws Exception
 46      */
 47     public static String getAccToken() throws Exception {
 48         // 先从redis取,取不到再从微信里面取
 49         String weixin_acc_token = RedisHelper.getStringValue(
 50                 "weixin_acc_token"2);
 51         if (StringUtils.isEmpty(weixin_acc_token)) {
 52             String resultStr = HttpURLConnectionUtil
 53                     .getWebHTMLCode(ACC_TOKEN_URL);
 54             JSONObject resultObj = JSONObject.parseObject(resultStr);
 55             String accToken = resultObj.getString("access_token");
 56             int expiresIn = resultObj.getIntValue("expires_in");
 57             // 写进redis
 58             RedisHelper.setStringValue("weixin_acc_token", accToken, expiresIn,
 59                     2);
 60             return accToken;
 61         }
 62         return weixin_acc_token;
 63 
 64     }
 65 
 66     /**
 67      * 获取微信票据
 68      * 
 69      * @return
 70      * @throws Exception
 71      */
 72     public static String getTicket() throws Exception {
 73         // 先从redis中取ticket,没有再从这里取
 74         String weixin_js_api_tiket = RedisHelper.getStringValue(
 75                 "weixin_js_api_tiket"2);
 76         if (StringUtils.isEmpty(weixin_js_api_tiket)) {
 77 
 78             String accToken = getAccToken();
 79             String resultStr = HttpURLConnectionUtil
 80                     .getWebHTMLCode(JSAPI_TICKET_URL + accToken);
 81             JSONObject resultObj = JSONObject.parseObject(resultStr);
 82             if (resultObj.getIntValue("errcode"== 0) {
 83                 String ticket = resultObj.getString("ticket");
 84                 int expires_in = resultObj.getIntValue("expires_in");
 85                 // 写入redis
 86                 RedisHelper.setStringValue("weixin_js_api_tiket", ticket,
 87                         expires_in, 2);
 88                 return ticket;
 89             }
 90             return null;
 91         }
 92         return weixin_js_api_tiket;
 93     }
 94 
 95     /**
 96      * 获取签名
 97      * 
 98      * @param timeStamp
 99      * @param requestUrl
100      * @return
101      * @throws Exception
102      */
103     public static String getSignature(Long timeStamp, String requestUrl)
104             throws Exception {
105         String ticket = getTicket();
106         StringBuffer allStr = new StringBuffer("jsapi_ticket=");
107         allStr.append(ticket).append("&noncestr=").append(NONCE_STR)
108                 .append("&timestamp=").append(timeStamp).append("&url=")
109                 .append(requestUrl);
110         MessageDigest crypt = MessageDigest.getInstance("SHA-1");
111         crypt.reset();
112         crypt.update(allStr.toString().getBytes("UTF-8"));
113         String signature = byteToHex(crypt.digest());
114         return signature;
115     }
116 
117     /**
118      * SHA1加密
119      * 
120      * @param hash
121      * @return
122      */
123     private static String byteToHex(final byte[] hash) {
124         Formatter formatter = new Formatter();
125         for (byte b : hash) {
126             formatter.format("%02x", b);
127         }
128         String result = formatter.toString();
129         formatter.close();
130         return result;
131     }
132 
133     /**
134      * 获取微信JS页面Config所需参数
135      * 
136      * @param timeStamp
137      * @param requestUrl
138      * @return
139      * @throws Exception
140      */
141     public static Map<String, Object> getConfig(long timeStamp,
142             String requestUrl) throws Exception {
143         Map<String, Object> map = new HashMap<String, Object>();
144         String signature = getSignature(timeStamp, requestUrl);
145         map.put("signature", signature);
146         map.put("nonceStr", NONCE_STR);
147         map.put("timestamp", timeStamp);
148         map.put("appId", APPID);
149         return map;
150     }
151 
152 }
153

再写一个接口类返回config中的参数:

 1 package com.freekeer.c.controller;
 2 
 3 import java.util.Date;
 4 import java.util.Map;
 5 
 6 import javax.servlet.http.HttpServletRequest;
 7 import javax.servlet.http.HttpServletResponse;
 8 
 9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11 import org.springframework.web.bind.annotation.RequestMapping;
12 import org.springframework.web.bind.annotation.RequestMethod;
13 import org.springframework.web.bind.annotation.RequestParam;
14 import org.springframework.web.bind.annotation.RestController;
15 
16 import com.freekeer.c.util.WeiXinUtils;
17 
18 /**
19  * 热搜词控制层
20  * 
21  * @author WuWei
22  */
23 @RestController
24 public class WeixinController extends BaseController {
25 
26     @SuppressWarnings("unused")
27     private static Logger log = LoggerFactory.getLogger(WeixinController.class);
28 
29     @RequestMapping(value = "/wx-config", method = { RequestMethod.GET })
30     public Map<String, Object> getConfig(HttpServletRequest request,
31             HttpServletResponse response, @RequestParam(value="url", required=true) String url) throws Exception {
32         long timeStamp = new Date().getTime()/1000;
33         Map<String, Object> result = WeiXinUtils.getConfig(timeStamp, url);
34         return result;
35     }
36     
37 }
38

JS端通过ajax调用

 1 function weixinShareZp(demandData) {
 2     var title = demandData.profession;
 3     var desc = demandData.projectName+",在"+demandData.workCityName+"招聘"+title+demandData.requireStaffCount+"";
 4     var url = "../wx-config?url="+encodeURIComponent(window.location.href);
 5     var logoUrl = "http://xxx-resource-public.oss-cn-beijing.aliyuncs.com/share_img/logo.png";
 6     $.get(url,[],function(data) {
 7         wx.config({
 8             debug:true,
 9             appId:data.appId,
10             timestamp:data.timestamp,
11             nonceStr:data.nonceStr,
12             signature:data.signature,
13             jsApiList: [
14                 'onMenuShareTimeline', 
15                 'onMenuShareAppMessage', 
16                 'onMenuShareQQ',
17                 'onMenuShareWeibo',
18                 'onMenuShareQZone'
19             ]
20         });
21         wx.ready(function(){ 
22             // 获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 
23             wx.onMenuShareTimeline({ 
24                 imgUrl:logoUrl,
25                   link:window.location.href,
26                   desc:desc,
27                   title:title,
28                   type:"link"
29             }); 
30             // 获取“分享给朋友”按钮点击状态及自定义分享内容接口 
31             wx.onMenuShareAppMessage({ 
32                 imgUrl: logoUrl,
33                   link: window.location.href,
34                   desc: desc,
35                   title: title,
36                   type: "link"
37             });
38             wx.onMenuShareQQ({
39                 title: title,
40                 desc: desc,
41                 link: window.location.href,
42                 imgUrl: logoUrl
43             });
44             wx.onMenuShareWeibo({
45                 title: title,
46                 desc: desc,
47                 link: window.location.href,
48                 imgUrl: logoUrl,
49             });
50             wx.onMenuShareQZone({
51                 title: title,
52                 desc: desc,
53                 link: window.location.href,
54                 imgUrl: logoUrl,
55             });
56         });
57 
58     });
59 }
开始通过微信分享进行测试吧!
posted on 2016-12-16 17:05 小人物_Amor 阅读(3512) 评论(0)  编辑  收藏 所属分类: javaweb

只有注册用户登录后才能发表评论。


网站导航: