前言

网页实现微信分享功能,这个其实在百度上是有很多例子的,而且写得也都还不错。不过我这个跟他们的不大一样。一般的博客会将分享需要的微信凭证这些写进一个项目中,本项目获取,本项目实现分享功能。而我是获取微信凭证是单独的一个项目,这样一个服务号的获取的微信凭证,可以提供给很多个项目使用,拓展性还是可以的,有需要的朋友可以参考下。

方案的选择

其实我刚开始做的时候想到两个方案:

**1、将获取的微信凭证放进static静态变量中。**具体做法是:web项目启动,然后启动监听器,利用初始化方法启动一个线程,这个线程就是定时发请求去获取微信凭证,然后保存到public static静态变量中,如果有需要的话直接去取。这个是我很早之前做发送微信模板消息考虑的做法,但是我使用main方法本次测试的时候,好像是取不到static的值,当时百度上很多人都说可以用这种方式,但是我测试的时候是取不到static 里面的值,当然感兴趣的朋友可以试一下,也许你会有不一样的发现。

2、方案二如前言所示。

项目总体实现思路

其实要实现一个服务号获取的凭证给多个应用或者项目共享,很简单。就是把获取微信凭证的这个项目独立出来,然后使用全局缓存的方式,每次其它项目需要的时候就发请求,参数中携带有时间戳,然后与缓存中的时间戳相比较,如果时间差超过两小时,那么重新获取微信凭证并保留在缓存中,如果时间差并未超过两小时,那么将缓存中保留的凭证返回。也可以这么理解,其它项目发请求过来获取微信凭证的时候,每次都是从缓存中取出微信凭证,至于要不要重新获取比较时间戳即可。

具体实现流程

1、 要使用微信的东西,首先当然是查看微信官方文档进行了解,对整个流程有个清晰的认识。

开发前的准备请看下图。

设置的时候请注意上面的文字

对于上面必须文件的放置,可以放到你填写的域名指向的tomcat下的/webapps/ROOT这个里面,访问域名+文件名比如说:wx.qq.com/MP_verify_TefDJNVX7f91MSYR.txt如果可以成功访问,那么恭喜你设置成功。

2、考虑到在开发的过程中需要调试,所以建议大家使用微信web开发者工具进行调试,这样效果比手机好得多。

下载工具之后,到公众平台——开发——开发者工具——web开发者工具 授权给自己的微信号,然后就可以使用微信web开发者工具进行相关的开发调试了。

3、需要获取的微信凭证
我当时记得官方有个具体的链接解释的,就是调用什么接口,返回参数和注意事项,但是我一时半会找不到了,所以我在这里罗列下。
首先是accessToken,具体可以查看获取access_token
然后使用accessToken获取api_ticket,在微信JS-SDK说明文档 微信卡券下的获取api_ticket这部分可以看到对应的信息。
获取这两个凭证的时候,我们注意到这两个有时间和次数限制,所以我的建议是存到全局缓存ServletContext对象中:

   ServletContext sc = request.getSession().getServletContext();sc.setAttribute("accessToken", accessToken);sc.setAttribute("jsapiTicket", jsapiTicket);

然后通过这样来取值就可以实现微信凭证共享的目的了。

accessToken = (String) sc.getAttribute(“accessToken”);
jsapiTicket = (String) sc.getAttribute(“jsapiTicket”);

4、请求的简单验证

作为一个网络接口,安全是一个很重要的考虑。所以我这里通过对参数进行校验来决定是否返回微信凭证。

String timestampString = Tools.GetString(request.getParameter("timestamp"), "");// 请求的时间戳String nonceStr = Tools.GetString(request.getParameter("nonceStr"), ""); //uuid生成的随机数,概率避免被恶意请求资源String auth = Tools.GetString(request.getParameter("auth"), "");// 密钥,避免恶意请求

这个部分比较简单,其中auth是对参数和密钥进行md5加密的字符串,获取字符串之后,将参数加密对比就行,无需解密对比(哈哈)。用springMVC就可以实现了,具体可以查看我的代码。下面来介绍重点部分。

5、后面这部分由于是我应用的一个小模块,所以我把这部分代码贴出来,大家可以自己整理下。

首先是访问页面,然后发请求获取凭证

//在访问视图的时候,同时发请求,获取和制作需要的信息
//controller
@RequestMapping(value = "/remindpage", method = { RequestMethod.GET, RequestMethod.POST })public ModelAndView login(@PathVariable("channel") String channel, @PathVariable("activity") String activity,HttpServletRequest request) throws Exception {ModelAndView mav = new ModelAndView(activity+"/remindpage");Map<String, Object> shareMap = WechatUtil.getWxConfig(activity,request);mav.addObject("appId", shareMap.get("appId"));mav.addObject("timestamp", shareMap.get("timestamp"));mav.addObject("nonceStr", shareMap.get("nonceStr"));mav.addObject("signature", shareMap.get("signature"));mav.addObject("requestUrl", shareMap.get("requestUrl"));mav.addObject("wechatShareTitle", shareMap.get("wechatShareTitle"));mav.addObject("wechatShareDesc", shareMap.get("wechatShareDesc"));mav.addObject("wechatSharePic", shareMap.get("wechatSharePic"));int joinUserNum = userService.countJoinUserNum(); //统计报名人数mav.addObject("joinUserNum", joinUserNum);String subscribe = Tools.GetString(request.getParameter("subscribe"), "");if(subscribe.equals("0")){mav.addObject("subscribe", "0");}return mav;}

工具类WechatUtil.java
发送请求前,使用base64加密的时候,有个小细节要注意下,当加密的字符串超过一定长度,会自动增加换行符号,所以请看Base64加密后有换行回车的解决办法

package com.aotain.wechat.utils;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;import org.apache.log4j.Logger;import sun.net.www.URLConnection;
import net.sf.json.JSONObject;public class WechatUtil {private static Logger Log = Logger.getLogger("sysLog");private static String AppId = Constants.getProValue("APPID");private static String WechatKey = Constants.getProValue("wechatKey");/*** 方法名:httpRequest</br> 详述:发送http请求</br> 开发人员:souvc </br> 创建时间:2016-1-5* </br>* * @param requestUrl* @param requestMethod* @param outputStr* @return 说明返回值含义* @throws 说明发生此异常的条件*/public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) throws Exception {requestUrl = requestUrl.replaceAll("[\\s*\t\n\r]", "");   //避免base64加密后自动回车换行过长自动换行JSONObject jsonObject = null;StringBuffer buffer = new StringBuffer();InputStream inputStream = null;InputStreamReader inputStreamReader = null;BufferedReader bufferedReader = null;HttpURLConnection httpUrlConn = null;try {URL url = new URL(requestUrl);httpUrlConn = (HttpURLConnection) url.openConnection();httpUrlConn.setDoOutput(true);httpUrlConn.setDoInput(true);httpUrlConn.setUseCaches(false);httpUrlConn.setRequestMethod(requestMethod);//httpUrlConn.setRequestProperty("content-type", "application/x-www-form-urlencoded");    if ("GET".equalsIgnoreCase(requestMethod))httpUrlConn.connect();if (null != outputStr) {OutputStream outputStream = httpUrlConn.getOutputStream();outputStream.write(outputStr.getBytes("UTF-8"));outputStream.close();}inputStream = httpUrlConn.getInputStream();inputStreamReader = new InputStreamReader(inputStream, "utf-8");bufferedReader = new BufferedReader(inputStreamReader);String str = null;while ((str = bufferedReader.readLine()) != null) {buffer.append(str);}jsonObject = JSONObject.fromObject(buffer.toString());} catch (Exception e) {Log.error("发送HTTP请求异常,请求地址:"+requestUrl+",请求方法:"+requestMethod+",请求结果:"+jsonObject);}finally{if(bufferedReader != null){bufferedReader.close();}if(inputStreamReader != null){inputStreamReader.close();}if(inputStream != null){inputStream.close();}httpUrlConn.disconnect();}return jsonObject;}/*** 方法名:getWxConfig</br> 详述:获取微信的配置信息 </br> 开发人员:souvc </br> 创建时间:2016-1-5* </br>* * @param request* @return 说明返回值含义* @throws Exception* @throws 说明发生此异常的条件*/public static Map<String, Object> getWxConfig(String activity,HttpServletRequest request) throws Exception {String WechatShareTitle = Constants.getProValue(activity+"_wechatShareTitle");  //配置文件中的分享标题String WechatShareDesc = Constants.getProValue(activity+"_wechatShareDesc");   //分享描述String WechatSharePic = Constants.getProValue(activity+"_wechatSharePic");    //分享的图片链接long timestamp = System.currentTimeMillis() / 1000; // 必填,生成签名的时间戳String nonceStr = UUID.randomUUID().toString(); // 必填,生成签名的随机串String beforeAuth = nonceStr +"$"+timestamp + "$" + WechatKey;String auth = Tools.getBase64Code(Tools.GetMD5Codes(beforeAuth)); //身份验证ServletContext sc = request.getSession().getServletContext();// 获取全局对象String  timestampStr = timestamp + "";String getWxConfigUrl = "你的存储有微信凭证的项目地址?nonceStr="+nonceStr+"&timestamp="+timestampStr+"&auth="+auth;Log.info("requestParam"+"|"+"timestamp="+timestamp+",nonceStr="+nonceStr+",auth="+auth+",getWxConfigUrl="+getWxConfigUrl);JSONObject WxConfigJson = WechatUtil.httpRequest(getWxConfigUrl,"GET",null);Log.info("WxConfigJson:"+WxConfigJson);Map<String, Object> shareMap = new HashMap<String, Object>();if(WxConfigJson != null  && WxConfigJson.getInt("resultCode") == 0){String accessToken = WxConfigJson.getString("accessToken");String jsapiTicket = WxConfigJson.getString("jsapiTicket");String requestUrl = request.getRequestURL().toString();String requestParam = request.getQueryString();//获取携带的参数if(requestParam != null){ requestUrl = requestUrl +"?"+ requestParam; //组成真正的URL/*  requestUrl = requestUrl +"?param=2"; //组成真正的URL*/          }String sign = "jsapi_ticket=" + jsapiTicket + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + requestUrl;MessageDigest crypt = MessageDigest.getInstance("SHA-1");crypt.reset();crypt.update(sign.getBytes("UTF-8"));String signature = byteToHex(crypt.digest());shareMap.put("appId", AppId);    // 注意这里参数名必须全部小写,且必须有序shareMap.put("timestamp", timestamp);shareMap.put("nonceStr", nonceStr);shareMap.put("signature", signature);shareMap.put("requestUrl", requestUrl);shareMap.put("wechatShareTitle", WechatShareTitle);shareMap.put("wechatShareDesc", WechatShareDesc);shareMap.put("wechatSharePic", WechatSharePic);shareMap.put("accessToken", accessToken);//微信凭证sc.setAttribute("accessToken", accessToken);//将凭证缓存起来,方便获取}return shareMap;}/*** 方法名:byteToHex</br> 详述:字符串加密辅助方法 </br> 开发人员:souvc </br> 创建时间:2016-1-5* </br>* * @param hash* @return 说明返回值含义* @throws 说明发生此异常的条件*/private static String byteToHex(final byte[] hash) {Formatter formatter = new Formatter();for (byte b : hash) {formatter.format("%02x", b);}String result = formatter.toString();formatter.close();return result;}}

Tools.java 中涉及的MD5加密和base64位加密,看我分享的代码即可。

最后是jsp页面的写法,全部的写法可以参考分享接口:我这里的写法是在jsp加载的时候将页面中的变量赋值,值从前面的controller传递过来的。这些东西写在body 和 html 标签即可,当然写在哪里都可以。注意要记得引进
http://res.wx.qq.com/open/js/jweixin-1.2.0.js

</body>
<script type="text/javascript">
// 微信信息的以及调用的配置
wx.config({debug: false, appId: '${appId}', timestamp: '${timestamp}', nonceStr: '${nonceStr}', signature: '${signature}',jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage']
});wx.ready(function(){// 获取“分享到朋友圈”按钮点击状态及自定义分享内容接口wx.onMenuShareTimeline({title: '${wechatShareTitle}', // 分享标题desc: "${wechatShareDesc}", // 分享描述link:"${requestUrl}",imgUrl: "${wechatSharePic}", // 分享图标type: 'link', // 分享类型,music、video或link,不填默认为link// 用户确认分享后执行的回调函数success: function () { // index.sub("clickShare");console.log("用户确认分享后执行的回调函数");},// 用户取消分享后执行的回调函数cancel: function () { console.log("用户取消分享后执行的回调函数");}});// 获取“分享给朋友”按钮点击状态及自定义分享内容接口wx.onMenuShareAppMessage({title: '${wechatShareTitle}', // 分享标题desc: "${wechatShareDesc}", // 分享描述link:"${requestUrl}",imgUrl: "${wechatSharePic}", // 分享图标type: 'link', // 分享类型,music、video或link,不填默认为link// 用户确认分享后执行的回调函数success: function () { // index.sub("clickShare");console.log("用户确认分享后执行的回调函数");},// 用户取消分享后执行的回调函数cancel: function () { console.log("用户取消分享后执行的回调函数");}});
});
</script>
</html>

使用微信分享接口还有小地方注意下,就是当前的页面的url是什么,配置中的link就是什么,比如说我这里是http:xxxxx.com/wechat/share.do(springmvc的转发,地址栏就是显示这样的地址),那么link对应的就是这个,注意是在controller发请求,而不是到了jsp再写下面这两行java代码,因为这样的url实际上jsp的地址,有可能会导致分享失败或者暴露资源地址。

Map<String, Object> shareMap = WechatUtil.getWxConfig(activity,request);

至此,微信分享部分已经完成。不过我这里还有个小缺陷,我这里是使用jsp页面加载,然后将微信的配置信息进行渲染,然后实现分享,但是这样只能使用微信的分享按钮。感兴趣的朋友可以研究下是否可以做成自定义一个分享按钮,点击按钮发送请求来获取参数,再将网页分享出去。

结语

由于部分代码是贴上去的,可能看的时候不太方便,所以有不明白或者有好的建议的朋友,都可以给我留言。这个功能我已经实现,我就是给有需要的朋友一个参考,抛砖引玉,将代码改成符合自己业务需要的。

全局存储微信凭证源代码地址,maven项目:
http://download.csdn.net/download/qq_32574435/10196662

实现微信自定义分享网页(java)相关推荐

  1. 微信自定义分享网页标题及内容

    通过向后台请求拿到appid,jsapi_ticket等必要参数: 网页导入微信js-Sdk微信开发文档 在wx.reday api中调用相关api; 直接上代码: html代码 <!DOCTY ...

  2. typecho图标_Typecho微信自定义分享插件WeChatShare 自定义标题描述小图标

    前言 张小龙带领微信走进了大众的生活,在十亿用户的生活里扎根.于是我们不管是阅读.聊天.看视频,基本上都离不开微信.好东西分享给用户,第一时间想到的肯定是微信. 很久以前,一个网页分享到微信聊天,系统 ...

  3. 微信自定义分享限制分享

    微信自定义分享&限制分享 一.微信自定义分享 [ (* ̄︶ ̄)微信官方文档 ] 业务需求: 开发过程中有些业务需要借助微信进行推广和宣传.难免需要使用微信提供的一些功能,比如微信的二次分享(也 ...

  4. 微信自定义分享卡片链接的解决方案(可自定义标题 描述 图片)

    微信自定义分享卡片链接的解决方案(可自定义标题 描述 图片) 参考文章: (1)微信自定义分享卡片链接的解决方案(可自定义标题 描述 图片) (2)https://www.cnblogs.com/rg ...

  5. 微信自定义分享功能实现

    微信自定义分享功能实现 微信自定义分享功能实现 一.实现的关键 1.后端的任务 2.前台的任务 二.实现具体步骤 1.js安全域名配置(被分享的网址必须实现) 2.添加服务器配置(成为开发者) 3.生 ...

  6. 微信自定义分享功能二次封装

    我原本是纯后端开发,但是最近因为工作原因,做了微信公众号的二次开发,说实话写H5调CSS调得头大,还是得术业有专攻才行.话不多说,因为要做微信自定义分享,网上其实已经有很多轮子了,但是100个人就有1 ...

  7. 微信自定义分享--失效问题

    微信自定义分享 微信自定义分享到朋友圈/朋友失效问题 微信自定义分享到朋友圈 wx.onMenuShareTimeline({title: '', // 分享标题link: '', // 分享链接,该 ...

  8. 微信自定义分享ios无效

    微信自定义分享无效情况有很多种,这里记录开发中遇到的一种 分享链接包含汉字 在自定义分享中,分享链接包含汉字,且没有进行编码处理,会导致ios无效:因为Android会自行进行处理,ios却不会,导致 ...

  9. 微信自定义分享pc正常手机不正常

    微信自定义分享pc正常手机不正常,经排查后发现微信自定义分享的链接不能直接使用授权链接.因此我的解决方法是,新建一个页面用于授权.就解决啦

最新文章

  1. 【机器学习入门笔记6:OpenCV像素的读取与写入】20190204
  2. CentOS下添加Root权限用户‘超级用户’方法(xxx is not in the sudoers file.This incident will be reported.的解决方法)
  3. 学术会议墙报_中国化学会第十四届全国电分析化学学术会议在南京顺利召开
  4. @RequestParam注解四个属性字段说明
  5. Python 基础课程第十一天
  6. ]MySQL操作命令语句实例
  7. idea插件JRebel激活
  8. 矩阵快速幂 超详细介绍
  9. html将图片裁剪成圆形,zrender将一张图片裁剪为圆形
  10. windows 远程连接mongo_远程连接天下数据ADSL动态拨号VPS教程(windows)
  11. Airbnb面试的27个奇葩问题,你 hold 住吗?
  12. EasyUI项目驱动学习
  13. java计算机毕业设计基于ssm的协同过滤算法的电影推荐系统(源代码+数据库+Lw文档)
  14. 985高校博士因文言文致谢走红!导师评价其不仅SCI写得好...
  15. 解决问题记录4:kettle数据库连接报错时区问题
  16. FPGA实现AD采集
  17. 重装office和关于office “很抱歉,遇到一些临时服务器问题。” 解决方法
  18. 原型图、交互设计、UI图的关系
  19. PHP隐形一句话后门,和ThinkPHP框架加密码程序
  20. C语言及gnuplot任意波形产生及作图-以三角波为例

热门文章

  1. Event Trace for Windows - 事件元数据总览 译(13)
  2. Java心理健康测试系统
  3. Failed to load local image resource /pages/pics/cloud://xxxxxxxxx.jpg
  4. 西安交大计算机和信息工程录取分数,西安交大在山西各专业录取分数.doc
  5. prf###.tmp临时文件导致磁盘资源不足
  6. 快速了解机器视觉(CV)基础知识
  7. C++基础用法—冒号的用法
  8. Linux内核中的IPSEC实现(1)
  9. 海康威视错误代码0xf_调用海康威视sdk获取车牌号
  10. 冯东阳:5000元葬送了我的行业站点之梦