微信JSSDK 使用

获取微信JSAPI_TICKET

package com.mazing.wx;

import com.fasterxml.jackson.core.type.TypeReference;
import com.mazing.CommonConstants;
import com.mazing.commons.utils.HttpClientUtils;
import com.mazing.commons.utils.JsonUtils;
import com.mazing.commons.utils.cfg.DesPropertiesEncoder;
import com.mazing.core.remote.config.Config;
import com.mazing.core.web.RestResult;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 读取微信 access token 的线程
 * 
 */
public class WxAccessTokenReaderThread extends Thread {

    private static final Logger logger = LoggerFactory.getLogger(WxAccessTokenReaderThread.class);

    private boolean running = true;

    /**
     * 获取间隔
     */
    private static final int INTERVAL = 2 * 60 * 1000;

    // access token 在 7200秒过期,
    private long TOKEN_TIMEOUT = 7200 * 1000L - INTERVAL;

    private long lastReadTime = 0L;

    private String appid = null;

    private String secret = null;

    /**
     * access token
     */
    static volatile String wxAccessToken = "";

    /**
     * jsapi_ticket
     */
    static volatile String wxJsapiTicket = "";

    public WxAccessTokenReaderThread() {
        super("WxAccessTokenReaderThread");
    }

    public void stopRunning() {
        running = false;
        interrupt();
    }

    @Override
    public void run() {
        logger.info("WxAccessTokenReaderThread started ...");
        if (!readConfig()) {
            logger.error("读取配置错误,线程结束,请配置 group=weixin_mp 的内容。 ");
            running = false;
        }
        while (running) {
            long now = System.currentTimeMillis();
            if (now - lastReadTime >= TOKEN_TIMEOUT) {
                try {
                    readToken();
                    readTicket();

                    lastReadTime = now;
                } catch (Exception e) {
                    logger.error("读取微信 access token / jsapi ticket 错误", e);
                }
            }
            try {
                sleep(INTERVAL);
            } catch (InterruptedException e) {
                break;
            }
        }
        logger.info("WxAccessTokenReaderThread stopped ...");
    }

    /**
     * 读取配置,只读一次
     * @throws Exception
     */
    public boolean readConfig() {
        String json = HttpClientUtils.doGet(CommonConstants.CONFIG_DOMAIN + "/api/base/allConfigs");
//        logger.info("wx#readConfig | http response | result: {}", json.substring(0, 10) + "***");
        RestResult<List<Config>> result = JsonUtils.parseObject(json, new TypeReference<RestResult<List<Config>>>() {
        });

        if (!(result.isSuccess())) {
            logger.warn("wx#readConfig#Failure | Failure Request | result: {}", json);
           return false;
        }

        final String groupCode = "weixin_mp";
        DesPropertiesEncoder decoder = new DesPropertiesEncoder();
        int found = 0;
        for (Config config : result.getObject()) {
            if (groupCode.equals(config.getGroupCode())) {
                if ("appid".equalsIgnoreCase(config.getConfigKey())) {
                    appid = decoder.decode(config.getConfigValue());
                    found++;
                }
                else if ("secret".equalsIgnoreCase(config.getConfigKey())) {
                    secret = decoder.decode(config.getConfigValue());
                    found++;
                }
            }
            if (found >= 2) break;
        }
        return found >= 2;
    }
    /**
     * 读取 access token
     */
    private void readToken() {
        Map<String, String> params = new HashMap<>(4);
        params.put("grant_type", "client_credential");
        params.put("appid", appid);
        params.put("secret", secret);
        String json = HttpClientUtils.doGet("https://api.weixin.qq.com/cgi-bin/token", 10000, params);
        if (StringUtils.isBlank(json)) {
            logger.error("读取微信 access token 错误");
        }
        Map<String, Object> map = JsonUtils.parseObject(json.trim(), new TypeReference<Map<String, Object>>() {
        });
        if (map.containsKey("access_token")) {
            wxAccessToken = (String) map.get("access_token");
            logger.info("读取微信 access token 成功");
            if (map.containsKey("expires_in")) {
                int expiresIn = ((Number)map.get("expires_in")).intValue();
                logger.info("过期时间 (s):" + expiresIn);
                TOKEN_TIMEOUT = expiresIn * 1000L - INTERVAL;
                if (TOKEN_TIMEOUT < 1000) {
                    TOKEN_TIMEOUT = 10 * 60 * 1000L - INTERVAL;
                }
            }

        }
        else {
            logger.error("读取微信 access token 错误:" + json);
        }
    }

    /**
     * 读取 jsapi_ticket
     */
    private void readTicket() {
        Map<String, String> params = new HashMap<>(4);
        params.put("access_token", wxAccessToken);
        params.put("type", "jsapi");
        String json = HttpClientUtils.doGet("https://api.weixin.qq.com/cgi-bin/ticket/getticket", 10000, params);
        Map<String, Object> map = JsonUtils.parseObject(json.trim(), new TypeReference<Map<String, Object>>() {
        });
        int errcode = -1;
        if (map.get("errcode") != null) {
            errcode = ((Number) map.get("errcode")).intValue();
            logger.info("读取微信 jsapi_ticket 结果,errcode: {}, errmsg: {}", errcode, map.get("errmsg"));
            if (errcode == 0) {
                wxJsapiTicket = (String) map.get("ticket");
                logger.info("读取微信 jsapi_ticket 成功!");
            }
        }
        else {
            logger.error("读取微信 jsapi_ticket 错误:" + json);
        }
    }
}






package com.mazing.commons.wx;

import com.mazing.cfg.GlobalSetting;
import com.mazing.commons.utils.HttpClientUtils;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 获取微信 access token。
 * 文档见:http://mp.weixin.qq.com/wiki/15/54ce45d8d30b6bf6758f68d2e95bc627.html
 * 接口:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
 * 每天最多 2000 次
 * @
 */
public class AccessTokenCollector extends Thread {

    private static final Logger logger = LoggerFactory.getLogger(AccessTokenCollector.class);
	// true:不需要读取wx token(线程直接结束)
	private final boolean skip = Boolean.parseBoolean(GlobalSetting.getSysCongfig("noTokenWX"));

    private boolean stopped = false;

    /**
     * 间隔
     */
    private static final int INTERVAL = 2 * 60 * 1000;

    // 读取 access token 的地址
    public static final String URL = "http://123.40.50.60:20090/";

    public AccessTokenCollector() {
        super("accessTokenCollector");
    }

    public void stopRunning() {
        this.stopped = true;
        interrupt();
    }

    @Override
    public void run() {
		if (skip) {
			logger.info("mobile#wx#AccessTokenCollector | 不需要读取微信公众号Token信息");
			return;
		}

    	logger.info("mobile#wx#AccessTokenCollector | 读取微信公众号 token 线程启动 ...");
        while (!stopped) {

            try {
                String str = HttpClientUtils.doGet(URL);
                str = StringUtils.trimToEmpty(str);
                if (StringUtils.isNotEmpty(str)) {
                    String[] array = str.split("\\|");
                    WeixinAccessToken.ACCESS_TOKEN = array[0];
                    WeixinAccessToken.JSAPI_TICKET = array[1];
                    logger.info("mobile#wx#AccessTokenCollector | 读取微信公众号 token 成功");
                } else {
                    logger.error("mobile#wx#AccessTokenCollector | 读取微信公众号 token 出错,没有结果");
                }
            } catch (Exception e) {
                logger.error("mobile#wx#AccessTokenCollector | 出错", e);
            }

            try {
                Thread.sleep(INTERVAL);
            } catch (InterruptedException e) {
                // ignore
                break;
            }
        }
        logger.info("mobile#wx#AccessTokenCollector | 读取微信公众号 token 线程结束 ...");
    }
}





微信JSSDK签名 数据

package com.mazing.mobile.web.utils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mazing.CommonConstants;
import com.mazing.commons.utils.JsonUtils;
import com.mazing.commons.utils.cfg.DesPropertiesEncoder;
import com.mazing.commons.utils.encrypt.EncryptUtil;
import com.mazing.commons.wx.WeixinAccessToken;
import com.mazing.core.remote.config.ConfigGroupConstants;
import com.mazing.core.remote.config.ConfigRemoteService;

/**
 * 微信分享的工具类
 * 
 * 
 */
public class WeiXinHelper {
	private static final Logger logger = LoggerFactory.getLogger(WeiXinHelper.class);

	/**
	 * 微信分享的attr
	 */
	public static final String WX_CONFIG_ATTR = "wxConfig";

	/**
	 * 微信appid
	 */
	private static String WX_APPID = null;

//	/**
//	 * 用户的请求是否是https的(进入到nginx的是否是https)(nginx upstream是http)
//	 */
//	public static boolean httpsFromNginx(HttpServletRequest request) {
//		String url = request.getRequestURL().toString();
//		String scheme = request.getHeader("x-forwarded-proto");
//		return (url.startsWith("https://") || (StringUtils.isNotBlank(scheme)) && "https".equals(scheme));
//	}

	/**
	 * 检查当前的请求是否是 https 的,如果不是则重定向成为https<br>
	 * 如果执行了重定向,返回true
	 */
	public static boolean redirectFullUrl2Https(HttpServletRequest request, HttpServletResponse response) throws IOException {
		String params = request.getQueryString();
		String url = request.getRequestURL().toString();
		String scheme = request.getHeader("x-forwarded-proto");

		boolean need2https = (url.startsWith("http://") // 如果是http的请求
				&& (StringUtils.isBlank(scheme) || !("https".equals(scheme))));// nginx上报的不是https协议

		// 重定向请求为https,并返回true
		if (need2https) {
			url = url.replaceFirst("http://", "https://");

			if (params != null)
				url += "?" + params;

			response.sendRedirect(url);
			return true;
		}

		return false;
	}

	/**
	 * 返回当前页面完整的 URL,包括“?”后面的参数
	 * 
	 * @param request
	 * @return
	 */
	public static String getFullUrl(HttpServletRequest request) {
		String params = request.getQueryString();
		String url = request.getRequestURL().toString();

		// nginx 的 upstream配置使用的是 http,导致外部是https的请求,来到代码却是http
		// nginx 会带上scheme参数(443端口才设置)
		String scheme = request.getHeader("x-forwarded-proto");
		if (StringUtils.isNotBlank(scheme) && "https".equals(scheme))
			url = url.replaceFirst("http://", "https://");

		if (params != null) {
			url += "?" + params;
		}
		return url;
	}

	public static void main(String[] args) {
		String url = "http://xxx.com?callback=http://123.com";
		System.out.println(url.replaceFirst("http", "https"));
	}

	/**
	 * 微信公众号的appid
	 * 
	 * @return
	 */
	private static String getWxMpAppId() {
		if (WX_APPID == null) {
			String encryptedId = ConfigRemoteService.getConfig(ConfigGroupConstants.WEIXIN_WP, "appid");
			if (encryptedId != null) {
				try {
					DesPropertiesEncoder decoder = new DesPropertiesEncoder();
					WX_APPID = decoder.decode(encryptedId);
				} catch (Exception e) {
					logger.error("mobile#share#getWxMpAppId | 解密 appid 出错", e);
				}
			} else {
				logger.error("mobile#share#getWxMpAppId | 没有配置 group_code: {} config_key: {} !!", ConfigGroupConstants.WEIXIN_WP, "appid");
			}
		}
		return WX_APPID;
	}

	/**
	 * 微信分享的 config
	 * 
	 * @param url 当前页面完整的地址,包括参数
	 * @return
	 */
	public static String getWxMpConfig(String url) {
		String noncestr = RandomStringUtils.random(16, CommonConstants.LETTER_NUMBER_CHARS);
		long timestamp = System.currentTimeMillis() / 1000; // 秒
		logger.info("mobile#share#getWxMpConfig | 设置微信分享配置 |url: {}", url);
		String appId = getWxMpAppId();
		String sourceStr = "jsapi_ticket=" + WeixinAccessToken.JSAPI_TICKET + "&noncestr=" + noncestr + "&timestamp=" + timestamp + "&url="
				+ url;

		String signature = EncryptUtil.getSHA1(sourceStr).toLowerCase();
		String[] jsApiList = new String[] { //
		"checkJsApi", //
				"onMenuShareTimeline", //
				"onMenuShareAppMessage",//
				"onMenuShareQQ",//
				"onMenuShareWeibo", //
				"onMenuShareQZone",//
				"hideAllNonBaseMenuItem",//
				"showAllNonBaseMenuItem" };
		Map<String, Object> map = new HashMap<>(8);
		map.put("nonceStr", noncestr);
		map.put("timestamp", timestamp);
		map.put("appId", appId);
		map.put("signature", signature);
		map.put("debug", false);
		map.put("jsApiList", jsApiList);
		return JsonUtils.toJSONString(map);
	}

	/**
	 * 设置微信分享JSSDK 需要的config配置信息<br>
	 * 
	 * 凡是需要设置分享操作菜单(分享至朋友圈、QQ、微博, 或者屏蔽分享功能,只保留设置字体、刷新 等 菜单) 相关的页面, 都需要调用该方法进行config设置
	 * 
	 * @param request
	 * 
	 */
	public static void setWxJsConfig(HttpServletRequest request) {
		// 微信相关的内容
		String url = WeiXinHelper.getFullUrl(request);

		request.setAttribute(WeiXinHelper.WX_CONFIG_ATTR, WeiXinHelper.getWxMpConfig(url));
	}

}








页面jsp, 本页面上以引入方式,调起微信的分享功能



<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<script type="text/javascript"
	src="https://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script type="text/javascript">
	// 微信分享的配置
	wx.config(<c:out value="${wxConfig}" escapeXml="false"/>);

	wx.error(function(res) {
		//alert(JSON.stringify(res));
	});

	/**
	 http://www.xiaomeiti.com/note/3561
	 **/
	function WeixinShare(shareData) {
		this.shareData = shareData;
		if (wx && wx.checkJsApi) {
			this.shareType = "api";
			this.initByAPI();
		} else {
			this.shareType = "bridge";
			this.initByBridge();
		}
	}

	WeixinShare.prototype.initByAPI = function() {
		var me = this;

		wx.ready(function() {

			var shareData = {
				title : me.getParam("title"),
				desc : me.getParam("desc"),
				link : me.getParam("link"),
				imgUrl : me.getParam("imgUrl"),
				trigger : function(res) {
					/*
					this.title = me.getParam("title");
					this.desc = me.getParam("desc");
					this.link = me.getParam("link");
					this.imgUrl = me.getParam("imgUrl");
					 */
				}
			};
			wx.onMenuShareAppMessage(shareData);

			var shareData2 = {
				title : me.getParam("title"),
				desc : me.getParam("desc"),
				link : me.getParam("link"),
				imgUrl : me.getParam("imgUrl"),
				trigger : function(res) {
				}
			};
			var timelineTitle = me.getParam("timelineTitle");
			if (timelineTitle) {
				shareData2.title = timelineTitle;
			}
			wx.onMenuShareTimeline(shareData2);//分享朋友圈
			wx.onMenuShareQQ(shareData);//分享至QQ
			wx.onMenuShareWeibo(shareData);//分享到微博
			wx.onMenuShareQZone(shareData);//分享到QZone
			
			
			//这里调用show all,是因为其他页面可能会调用了hide all(貌似是全局生效) 后跳转至 需要show all的页面
			wx.showAllNonBaseMenuItem({
				success : function() {
					//MazingEnv.log('已显示所有非基本菜单项');
				}
			});
		});
	};

	WeixinShare.prototype.initByBridge = function() {
		var me = this;
		document.addEventListener('WeixinJSBridgeReady',
				function onBridgeReady() {
					window.alert('Bridge triggered.');
					WeixinJSBridge.on('menu:share:appmessage', function(argv) {
						me.shareFriend()
					});
					WeixinJSBridge.on('menu:share:timeline', function(argv) {
						me.shareTimeline()
					});
				}, false);
	};

	WeixinShare.prototype.getParam = function(name) {
		var val = this.shareData[name];
		if (typeof val == "function") {
			return val();
		}

		return val;
	};

	WeixinShare.prototype.shareFriend = function() {
		WeixinJSBridge.invoke('sendAppMessage', {
			appid : this.getParam("appid"),
			img_url : this.getParam("imgUrl"),
			img_width : 120,
			img_height : 120,
			link : this.getParam("link"),
			title : this.getParam("title"),
			desc : this.getParam("desc")
		}, function(res) {
			_report('send_msg', res.err_msg);
		});
	};

	WeixinShare.prototype.shareTimeline = function() {
		WeixinJSBridge.invoke('shareTimeline', {
			appid : this.getParam("appid"),
			img_url : this.getParam("imgUrl"),
			img_width : 120,
			img_height : 120,
			link : this.getParam("link"),
			title : this.getParam("title"),
			desc : this.getParam("desc")
		}, function(res) {
			_report('timeline', res.err_msg);
		});
	};

	var wxApi = new WeixinShare(shareData);
</script>




隐藏微信的分享功能

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<script type="text/javascript"
	src="https://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script type="text/javascript">
	// 微信分享的配置
	wx.config(<c:out value="${wxConfig}" escapeXml="false"/>);

	wx.error(function(res) {
		//alert(JSON.stringify(res));
	});

	wx.ready(function() {
		// 1 判断当前版本是否支持指定 JS 接口,支持批量判断
		wx.checkJsApi({
			jsApiList : [ 'hideAllNonBaseMenuItem' ],
			success : function(res) {
				MazingEnv.log('当前版本支持wx JS接口');
			}
		});

		// 8.3 批量隐藏菜单项
		/*
		  wx.hideMenuItems({
		    menuList: [
		      'menuItem:readMode', // 阅读模式
		      'menuItem:share:timeline', // 分享到朋友圈
		      'menuItem:copyUrl' // 复制链接
		    ],
		    success: function (res) {
		      alert('已隐藏“阅读模式”,“分享到朋友圈”,“复制链接”等按钮');
		    },
		    fail: function (res) {
		      alert(JSON.stringify(res));
		    }
		  });
		 */

		// 8.5 隐藏所有非基本菜单项
		wx.hideAllNonBaseMenuItem({
			success : function() {
				MazingEnv.log('已隐藏所有非基本菜单项');
			}
		});

	});
</script>



猜你喜欢

转载自xiaoliang330.iteye.com/blog/2301037