本文分成两部分来阐述使用微信JS-SDK进行自定义分享功能:
1.个人以及开发环境的自测
2.正式生产环境的部署
============================================================================
1.个人用户以及开发环境的自测可以使用微信提供的开发者工作进行分享功能的测试,步骤如下:
1、登录微信公众号后台,在最后一个配置项里,点开发者工具进入以下界面
2、选择公众平台测试账号,接下来就可以愉快地使用该测试账号模拟各种微信SDK提供的功能了。
3、接下来是测试账号中的各种配置信息的具体含义以及一些注意事项,需要额外注意的是,接口配置信息中的URL地址是供微信拿来校验测试账号的,并非我们通常所知道的获取微信acess_token的接口,这段代码在下面的文章中也会有详细解释,配置好这些信息之后,我们再按照提示关注一下测试公众号,完成以上几步,即可正常调用JS-SDK里的很多功能了。
2.正式生产环境的部署
以上是我们用来本地自测的步骤以及注意事项,接下来我们来详细谈谈如何在生产环境上部署。其实仔细查看微信提供的API即可实现,只是有几点可能需要特别注明的事项,比较容易采坑。
1、设置ip白名单:
2、首先我们点击公众号后台侧边栏的设置选项里的公众号设置,进入如下界面:
3、点击功能设置,设置js接口安全域名,并且按照提示下载一个验证.txt文件放入项目的跟路径下。
4、在需要调用分享的页面上引入:
<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
如果页面启用了https,务必要引入https://res.wx.qq.com/open/js/jweixin-1.0.0.js,否则将无法在iOS9.0以上系统中成功使用JSSDK。
完成以上这些操作即可完成微信分享的配置。
=======================================================================================
代码部分以下:
1、以下是详细的前端代码
$.ajax({
url:HTTPurls, //调用后台获取签名以及一些必要信息
type:"post",
dataType:"json",
cache:false,
data:{
url:window.location.href //此处的url最好动态获取以免签名失效
},
success:function(data) {
wx.config({
debug: false, // 开启调试模式,可使用微信开发者工具进行调试
appId: data.appId, // 必填,公众号的唯一标识
timestamp: data.timestamp,// 必填,生成签名的时间戳
nonceStr: data.nonceStr,// 必填,生成签名的随机串
signature: data.signature,// 必填,签名
jsApiList: [
'onMenuShareTimeline',
'onMenuShareAppMessage',
'onMenuShareQQ'
] // 必填,需要使用的JS接口列表
});
wx.ready(function () {
wx.onMenuShareTimeline({
title: '~',
desc:'',
link: window.location.href,
imgUrl: HTTPurls
});
wx.onMenuShareAppMessage({
title: '',
desc:'',
link: window.location.href,
imgUrl: HTTPurls
});
wx.onMenuShareQQ({
title: '',
desc:'',
link: window.location.href,
imgUrl: HTTPurls
});
});
}
});
2、以下是后台的代码,主要分为两部分,测试账号验证代码以及获取acess_token代码
测试账号配置验证代码:
public void checkSignature(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
// 开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数
String signature = request.getParameter("signature");// 微信加密签名(token、timestamp、nonce。)
String timestamp = request.getParameter("timestamp");// 时间戳
String nonce = request.getParameter("nonce");// 随机数
String echostr = request.getParameter("echostr");// 随机字符串
PrintWriter out = response.getWriter();
// 将token、timestamp、nonce三个参数进行字典序排序
String[] params = new String[] { token, timestamp, nonce };
Arrays.sort(params);
// 将三个参数字符串拼接成一个字符串进行sha1加密
String clearText = params[0] + params[1] + params[2];
String algorithm = "SHA-1";
String sign = new String(
Hex.encodeHex(MessageDigest.getInstance(algorithm).digest((clearText).getBytes()), true));
// 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
if (signature.equals(sign)) {
response.getWriter().print(echostr);
}
} catch (Exception e) {
log.error(e.getMessage());
}
}
获取access_token代码:
/**
* 获取sign
*
* @param jsapi_ticket
* @param url
* @return
*/
public static Map<String, String> sign(String jsapi_ticket, String url, String appId) {
Map<String, String> ret = new HashMap<String, String>();
String nonce_str = create_nonce_str();
String timestamp = create_timestamp();
String string1;
String signature = "";
StringBuilder sb = new StringBuilder(100);
//参数必须全小写
sb.append("jsapi_ticket=").append(jsapi_ticket).append("&noncestr=")
.append(nonce_str).append("×tamp=").append(timestamp)
.append("&url=").append(url);
string1 = sb.toString();
try {
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(string1.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
} catch (NoSuchAlgorithmException e) {
logger.error(e.getMessage());
} catch (UnsupportedEncodingException e) {
logger.error(e.getMessage());
}
ret.put("url", url);
ret.put("appId", appId);
ret.put("jsapi_ticket", jsapi_ticket);
ret.put("nonceStr", nonce_str);
ret.put("timestamp", timestamp);
ret.put("signature", signature);
System.out.println(ret.toString());
return ret;
}
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;
}
private static String create_nonce_str() {
return UUID.randomUUID().toString();
}
private static String create_timestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
public static String getAccessToken(String appId,String secret) {
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + secret;
HTTPRes ret = HttpUtils.doGet(url,new HashMap<String, String>());
String accessToken = JSONObject.parseObject(ret.getBody()).getString("access_token");
if (accessToken == null) {
throw new TengException("","获取AccessToken失败,微信返回值 ======>>>> " + ret.getBody());
}
return accessToken;
}
public static String getJsapiTicket(String appId,String secret) {
String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token="+getAccessToken(appId,secret);
HTTPRes ret = HttpUtils.doGet(url,new HashMap<String, String>());
String ticket = JSONObject.parseObject(ret.getBody()).getString("ticket");
if (ticket == null) {
throw new TengException("","获取JsapiTicket失败,微信返回值 ======>>>> " + ret.getBody());
}
return ticket;
}
============================================================================
在整体研发过程中,遇到如下问题:
1、部署到开发环境后的二次分享自定义失效,报Invalid signature,排查原因是分享出去后微信会自动带上一串标识“?from=groupmessage&isappinstalled=0”,在后台处理中将“&”进行了转义,导致当前页面的url和生成签名的url不一致,签名失效。所以发生签名失效的情况下,可检查一下两处url是否一致。使用微信提供的点击打开链接签名校验工具可以进行比对。(简直是深深坑!)
2、在微信开发者工具中偶现wx.ready方法无法调用的情况,生产环境未重现,网上查说是因为浏览器原因,没做深究,有兴趣者自行研究吧 =。 =