目录

微信分享实现

需求

在微信客户端中打开页面,点击右上角分享到朋友圈或者分享给朋友时候,希望可以自定义【抬头+描述+图片+链接】,而不是微信去截取当前页面的链接地址(有可能带一堆参数,而且每个整个活动中每个页面的地址可能都不一样),截取当前页面的title等。

示意图,如下:(上半部分为分享给朋友的样式,下半部分为分享到朋友圈的样式)

准备

  1. 服务号账号:cinvestors@tebon.com.cn
  2. 登录微信服务号后台,查看基础设置中,获得:AppID(应用ID)+AppSecret(应用密钥)
  3. 确认已经申请以下接口
    1. 获取“分享到朋友圈”按钮点击状态及自定义分享内容接口
    2. 获取“分享给朋友”按钮点击状态及自定义分享内容接口

基础开发

wx-share.js

这个js将被需要的页面依赖,上线前可以修改这个js中的【抬头+描述+图片+链接】

wx-share.js
//微信分享
var wxShare = (function () {
    var appId = 'wx2767b3767363f90e';//公众号APPID
    var title = '德邦证券放“大招”猴年春晚发红包';//抬头
    var desc = '没有模糊玻璃,无需花钱即可抢红包咯~';//描述
    var imgUrl = 'http://test.tebon.com.cn/trans/images/award/img_hb.gif';//图片
    var link = 'http://test.tebon.com.cn/trans/award/manager/index.htm';//链接
    var timestamp = '';//时间戳 后台生成
    var nonceStr = '';//随机串 后台生成
    var signature = '';//签名 后台生成
 
    var _init = function (_title, _desc) {
        getSign();
    };
 
    var getSign = function () {
        var reurl = location.href.split('#')[0];
        var para = {appid: appId, url: reurl};
        $.ajax({
                type: "post",
                data: para,
                async: true,
                url: contextPath + "/award/wxShare/wxShareSign.htm",
                dataType: "json",
                success: function (data) {
                    if (data.result == "true") {
                        timestamp = data.timestamp;
                        nonceStr = data.noncestr;
                        signature = data.signature;
                        wxConfig();
                        wxReady();
                        wxError();
                    } else {
                    	alert(data.message);
                    }
                }
                ,
                error: function (xhr, errorType, error) {
                }
            }
        )
        ;
 
    };
 
    var wxConfig = function () {
        wx.config({
            debug: false,
            appId: appId,
            timestamp: timestamp,
            nonceStr: nonceStr,
            signature: signature,
            jsApiList: [
                'onMenuShareTimeline',
                'onMenuShareAppMessage'
            ]
        });
    };
 
    var wxReady = function () {
        wx.ready(function () {
            //发送给朋友
            wx.onMenuShareAppMessage({
                title: title,
                desc: desc,
                link: link,
                imgUrl: imgUrl,
                trigger: function (res) {
                    //alert('用户点击发送给朋友');
                },
                success: function (res) {
                    //alert('已分享');跳转到排行榜页面
                    //window.location.href = 'revenue_ranking.html';
                },
                cancel: function (res) {
                    //alert('已取消');
                },
                fail: function (res) {
                    //alert(JSON.stringify(res));
                }
            });
 
            //分享到朋友圈
            wx.onMenuShareTimeline({
                title: title,
                desc: desc,
                link: link,
                imgUrl: imgUrl,
                trigger: function (res) {
                    //alert('用户点击分享到朋友圈');
                },
                success: function (res) {
                    //alert('已分享');
                    //window.location.href = 'revenue_ranking.html';
                },
                cancel: function (res) {
                    //alert('已取消');
                },
                fail: function (res) {
                    //alert(JSON.stringify(res));
                }
            });
 
 
        });
    };
 
    var wxError = function () {
        wx.error(function (res) {
            alert(res.errMsg);
        });
    };
 
    return {
        init: _init
    };
})();
 
//执行init
wxShare.init();

AwardWxShareController.java

微信要求,生成签名,需要在服务端生成,所以这个服务端类的用途是用于生成签名。

逻辑如下:

  1. 如果缓存中有ticket,而且没有过期,则直接用ticket
  2. 否则获取token
  3. 再根据token获取ticket
  4. 设置ticket缓存,设置失效的毫秒数,当前时间+7200-100秒,以免正好差个几秒钟的误差
  5. 生成签名,返回前端
AwardWxShareController.java
/*
 * @(#)GateInfoAction.java 1.0 2014-7-25下午4:17:49
 *
 * 德邦证券股份有限公司
 * Copyright (c) 2012-2014 Tebon, Inc. All rights reserved.
 */
 
package com.tebon.trans.web.award;
 
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
 
import javax.servlet.http.HttpServletRequest;
 
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
 
import com.tebon.sirius.cache.CacheFactoryProvider;
import com.tebon.sirius.cache.ICache.CacheType;
import com.tebon.sirius.utils.HttpClientUtils;
import com.tebon.sirius.utils.JSonUtils;
import com.tebon.sirius.utils.RandomGUIDUtils;
import com.tebon.trans.web.BaseAbstractController;
 
/**
 * 大转盘抽奖接口相关功能
 * 
 * @author liyb
 */
@Controller
@RequestMapping("/award/wxShare/")
public class AwardWxShareController extends BaseAbstractController {
 
	/**
	 * 放redis缓存微信分享ticket 并且设置失效时间(毫秒数)
	 */
	private static final String REDIS_WX_SHARE_TICKET = "redis.wx.share.ticket";
	private static final String REDIS_WX_SHARE_TICKET_DEAD_DATETIME = "redis.wx.share.ticket.dead.datetime";
 
	@Autowired
	CacheFactoryProvider cacheFactoryProvider;
 
	/**
	 * 微信分享生成签名逻辑
	 * @return
	 */
	@RequestMapping(value = "/wxShareSign")
	public @ResponseBody Map<String, String> testWxShareSign(HttpServletRequest request) throws Exception {
		/**
		 * 微信分享生成签名逻辑
		 * 0.准备结果MAP
		 */
		Map<String, String> map = new HashMap<String, String>();
		/**
		 * 1.如果缓存中有ticket,而且没有过期,则直接用ticket
		 */
		String ticket = (String)cacheFactoryProvider.getCacheProvider(CacheType.REDIES).get(REDIS_WX_SHARE_TICKET);
		Long deadDateTime = (Long)cacheFactoryProvider.getCacheProvider(CacheType.REDIES).get(REDIS_WX_SHARE_TICKET_DEAD_DATETIME);
		log.info("从redis获取ticket:" + ticket);
		if(StringUtils.isBlank(ticket) || new Date().getTime() > deadDateTime.longValue()){
			/**
			 * 2.否则获取token
			 * https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx2767b3767363f90e&secret=e42c1933553c1da62187d76379284fd1
			 * {
			 * 		"access_token": "E-zdrtVh4qgrNO5n0f2NWmM0z0_XxYey_2X6jHaiDFp7SQl-sMK4aoZT1FDAKO-_U9EF2h7pHe7i0WIuKsVp8RA9qaN7n-zZ9wJx91PB-RMARLdAIALCW",
			 * 		"expires_in": 7200
			 * }
			 */
			String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx2767b3767363f90e&secret=e42c1933553c1da62187d76379284fd1";
			String jsonString = HttpClientUtils.getWebContentByGet(url);
			log.info("获取access_token返回:" + jsonString);
			WxShareResponse wxShareResponse = JSonUtils.nonDefaultMapper().fromJson(jsonString, WxShareResponse.class);
			if(StringUtils.isBlank(wxShareResponse.getAccess_token())){
				map.put("result", "false");
				map.put("message", "微信分享准备失败!");
				return map;
			}
			/**
			 * 3.再根据token获取ticket
			 * https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=E-zdrtVh4qgrNO5n0f2NWmM0z0_XxYey_2X6jHaiDFp7SQl-sMK4aoZT1FDAKO-_U9EF2h7pHe7i0WIuKsVp8RA9qaN7n-zZ9wJx91PB-RMARLdAIALCW&type=jsapi
			 * {
			 * 		"errcode": 0,
			 * 		"errmsg": "ok",
			 * 		"ticket": "sM4AOVdWfPE4DxkXGEs8VDqK83ldYtSiGPegbeUXAElohlSKVPXjtbn6JqV5T2Nta1pqIl2b-uh1sapcge3SfQ",
			 * 		"expires_in": 7200
			 * }
			 */
			url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + wxShareResponse.getAccess_token() + "&type=jsapi";
			jsonString = HttpClientUtils.getWebContentByGet(url);
			log.info("获取ticket返回:" + jsonString);
			wxShareResponse = JSonUtils.nonDefaultMapper().fromJson(jsonString, WxShareResponse.class);
			if(StringUtils.isBlank(wxShareResponse.getTicket())){
				map.put("result", "false");
				map.put("message", "微信分享准备失败!");
				return map;
			}
			/**
			 * 4.设置ticket缓存,设置失效的毫秒数,当前时间+7200-100秒,以免正好差个几秒钟的误差
			 */
			ticket = wxShareResponse.getTicket();
			long expiresIn = wxShareResponse.getExpires_in();
			cacheFactoryProvider.getCacheProvider(CacheType.REDIES).put(REDIS_WX_SHARE_TICKET, ticket);
			cacheFactoryProvider.getCacheProvider(CacheType.REDIES).put(REDIS_WX_SHARE_TICKET_DEAD_DATETIME, new Date().getTime() + (expiresIn - 100)*1000);
		}
		/**
		 * 5.生成签名,返回前端
		 */
		long timestamp = new Date().getTime();
		String appid = request.getParameter("appid");
		String url = request.getParameter("url");
		String noncestr = new RandomGUIDUtils().toString();
		log.info("timestamp=" + timestamp + ",appid=" + appid + ",url=" + url + ",noncestr=" + noncestr + ",ticket=" + ticket);
		String signString = "jsapi_ticket=" + ticket + "&noncestr=" + noncestr + "&timestamp=" + timestamp + "&url=" + url;
		String sign = sha1Digest(signString);
		map.put("result", "true");
		map.put("timestamp", StringUtils.EMPTY + timestamp);
		map.put("noncestr", noncestr);
		map.put("signature", sign);
		return map;
	}
 
	public String sha1Digest(String signString) {
		try {
            MessageDigest digest = java.security.MessageDigest
                    .getInstance("SHA");
            digest.update(signString.getBytes());
            byte messageDigest[] = digest.digest();
            // Create Hex String
            StringBuffer hexString = new StringBuffer();
            // 字节数组转换为 十六进制 数
            for (int i = 0; i < messageDigest.length; i++) {
                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            return hexString.toString();
 
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
	}
}
 
/**
 * <dl>
 *    <dt><b>Title:</b></dt>
 *    <dd>
 *    	none
 *    </dd>
 *    <dt><b>Description:微信返回数据对象</b></dt>
 *    <dd>
 *    	<p>none
 *    </dd>
 * </dl>
 *
 * @author Administrator
 * @version 1.0, 2016年1月28日
 * @since trans
 *
 */
class WxShareResponse {
	String access_token;
	long expires_in;
 
	String errcode;
	String errmsg;
	String ticket;
	public String getAccess_token() {
		return access_token;
	}
	public void setAccess_token(String access_token) {
		this.access_token = access_token;
	}
	public long getExpires_in() {
		return expires_in;
	}
	public void setExpires_in(long expires_in) {
		this.expires_in = expires_in;
	}
	public String getErrcode() {
		return errcode;
	}
	public void setErrcode(String errcode) {
		this.errcode = errcode;
	}
	public String getErrmsg() {
		return errmsg;
	}
	public void setErrmsg(String errmsg) {
		this.errmsg = errmsg;
	}
	public String getTicket() {
		return ticket;
	}
	public void setTicket(String ticket) {
		this.ticket = ticket;
	}
}

分享页开发

需要分享的页面的开发,按道理整个活动过程中的每个页面,都要按如下方法来开发,达到自定义分享【抬头+描述+图片+链接】的效果。

在页面的head头部加入以下代码

<head>
	<!-- 微信分享依赖js -->
	<script>
		var contextPath = "${rc.contextPath}";
	</script>
	<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
	<script src="${Application["trans.static.resource.url"]}js/jquery-1.11.1.min.js"></script>
	<script src="${Application["trans.static.resource.url"]}js/wx-share.js"></script>
</head>

效果

分享给朋友

分享到朋友圈