/**
- @Date 28/10/2018
- @description 微信好友/朋友圈分享时自定义标题、描述、缩略图
- @wrong (1).配置js接口安全域名时,总是提示文件未放到服务器根目录
-
(2).分享后的链接,再次进去时部分数据加载不全面(未发送Ajax请求)
- @lesson (1).配置js接口安全域时,项目端口不能是80;
-
(2).对于需要进行分享的页面,Ajax请求中的参数传递不能放到data:{}中,可添加到url中进行传递。
*/
源码<index.html、WeChatBo.java、WeChatController.java>
<index.html>
<!DOCTYPE html>
<html>
......................
<script src="js/jquery-1.8.3.min.js"></script>
<script typet="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
<script src="js/WeChat.js"></script>
$(function(){
//var path='${path}';
refreshUrl();//刷新页面去除一次分享带的参数
var url = location.href.split('#').toString();//url不能写死
mui.ajax({
type : "post",
url : path+"/mvc/wechat/wechatParam",
data:{
url:url
},
success : function(result) {
var data = result.data;
wx.config({
debug: false,生产环境需要关闭debug模式
appId: data.appid,//appId通过微信服务号后台查看
timestamp: data.timestamp,//生成签名的时间戳
nonceStr: data.nonceStr,//生成签名的随机字符串
signature: encodeURIComponent(data.signature),//签名
jsApiList: [//需要调用的JS接口列表
'checkJsApi',//判断当前客户端版本是否支持指定JS接口
'onMenuShareTimeline',//分享给好友
'onMenuShareAppMessage'//分享到朋友圈
]
});
},
error: function(xhr, status, error) {
}
});
var invite_uid = fnGetQueryString('invite_uid');
wx.ready(function () {
var link = window.location.href;
var protocol = window.location.protocol;
var host = window.location.host;
//分享朋友圈
wx.onMenuShareTimeline({
title: '自定义标题',
link: location.href.split("#")[0]+'?invite_uid=' + invite_uid,
imgUrl: 'http://............/wxfx.png',// 自定义图标
trigger: function (res) {
//alert('click shared');
},
success: function (res) {
//alert('shared success');
//some thing you should do
},
cancel: function (res) {
//alert('shared cancle');
},
fail: function (res) {
//alert(JSON.stringify(res));
}
});
//分享给好友
wx.onMenuShareAppMessage({
title: "自定义标题", // 分享标题
desc: "自定义描述", // 分享描述
link: location.href.split("#")[0]+'?invite_uid=' + invite_uid, // 分享链接,该链接域名或路径必须与当前页面对应
imgUrl: ‘图片地址为服务器地址', // 自定义图标
type: 'link', // 分享类型,music、video或link,不填默认为link
dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
success: function () { // 用户确认分享后执行的回调函数
},
cancel: function () {
// 用户取消分享后执行的回调函数
}
});
wx.error(function (res) {
//alert(res.errMsg);
});
});
});
function fnGetQueryString(key) {
//正则获取url后面的参数值,如?env=dev&exp=123
var reg = new RegExp("(^|&)" + key + "=([^&]*)(&|$)");
var result = window.location.search.substr(1).match(reg);
return result ? decodeURIComponent(result[2]) : false;
}
function funcUrlDel(name) {
//删除url指定参数名并返回新的url
var loca = window.location;
var baseUrl = loca.origin + loca.pathname + "?";
var query = loca.search.substr(1);
if (query.indexOf(name) > -1) {
var obj = {};
var arr = query.split("&");
for (var i = 0; i < arr.length; i++) {
arr[i] = arr[i].split("=");
obj[arr[i][0]] = arr[i][1];
};
delete obj[name];
var url = baseUrl + JSON.stringify(obj).replace(/["{}]/g, "").replace(/:/g, "=").replace(/,/g, "&");
return url;
}
}
function getlinkSearch(key, reqStr) {
var reg = new RegExp("(^|&)" + key + "=([^&]*)(&|$)");
var result = reqStr.substr(1).match(reg);
return result ? decodeURIComponent(result[2]) : false;
}
function refreshUrl() {
//强制刷新到不带二次分享参数页面
var url = "", reqStr = "";
if (fnGetQueryString('from')) {
//from为微信二次分享自带参数
url = funcUrlDel('from');
var reqIndex = url.indexOf('?');
reqStr = url.substr(reqIndex);//截取去除from参数后的地址
if (getlinkSearch('isappinstalled', reqStr)) {
//isappinstalled为微信二次分享自带参数
url = url.substr(0, url.indexOf('?'));
//截取去除isappinstalled参数后的地址
window.location.href = url;
} else {
window.location.href = url;
}
}
}
</html>
<WeChatBo.java>
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "wechat")
public class WeChatBo {
private String appId; //开发者ID
private String appSecret; //开发者密码
private String accessTokenUrl;
private String apiTicketUrl;
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getAppSecret() {
return appSecret;
}
public void setAppSecret(String appSecret) {
this.appSecret = appSecret;
}
public String getAccessTokenUrl() {
return accessTokenUrl;
}
public void setAccessTokenUrl(String accessTokenUrl) {
this.accessTokenUrl = accessTokenUrl;
}
public String getApiTicketUrl() {
return apiTicketUrl;
}
public void setApiTicketUrl(String apiTicketUrl) {
this.apiTicketUrl = apiTicketUrl;
}
}
<WeChatController.java>
import com.insight.hr.mobile.pojo.bo.WeChatBo;
import com.insight.hr.mobile.util.HttpUtils;
import com.insight.hr.mobile.util.ResponseModel;
import org.apache.commons.lang.StringUtils;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import static com.ciic.base.core.constants.BaseConstants.URL_PREFIX_DATA;
@Controller
@RequestMapping(URL_PREFIX_DATA + "/wechat")
public class WeChatController {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Autowired
private WeChatBo weChatBo;
//微信参数
private String accessToken;
private String jsApiTicket;
//获取参数的时刻
private Long getTiketTime = 0L;
private Long getTokenTime = 0L;
//参数的有效时间,单位是秒(s)
private Long tokenExpireTime = 0L;
private Long ticketExpireTime = 0L;
//获取微信参数
@RequestMapping("/wechatParam")
@ResponseBody
public ResponseModel getWechatParam(String url) throws UnsupportedEncodingException {
//System.out.println(java.net.URLDecoder.decode(url,"UTF-8"));
//当前时间
long now = System.currentTimeMillis();
log.info("currentTime====>"+now+"ms");
//System.out.println(weChatBo);
//判断accessToken是否已经存在或者token是否过期
if(StringUtils.isBlank(accessToken)||(now - getTokenTime > tokenExpireTime*1000)){
JSONObject tokenInfo = getAccessToken();
if(tokenInfo != null){
log.info("tokenInfo====>"+tokenInfo.toString());
accessToken = tokenInfo.getString("access_token");
tokenExpireTime = tokenInfo.getLong("expires_in");
//获取token的时间
getTokenTime = System.currentTimeMillis();
log.info("accessToken====>"+accessToken);
log.info("tokenExpireTime====>"+tokenExpireTime+"s");
log.info("getTokenTime====>"+getTokenTime+"ms");
}else{
log.info("====>tokenInfo is null~");
log.info("====>failure of getting tokenInfo,please do some check~");
}
}
//判断jsApiTicket是否已经存在或者是否过期
if(StringUtils.isBlank(jsApiTicket)||(now - getTiketTime > ticketExpireTime*1000)){
JSONObject ticketInfo = getJsApiTicket();
if(ticketInfo!=null){
log.info("ticketInfo====>"+ticketInfo.toString());
jsApiTicket = ticketInfo.getString("ticket");
ticketExpireTime = ticketInfo.getLong("expires_in");
getTiketTime = System.currentTimeMillis();
log.info("jsApiTicket====>"+jsApiTicket);
log.info("ticketExpireTime====>"+ticketExpireTime+"s");
log.info("getTiketTime====>"+getTiketTime+"ms");
}else{
log.info("====>ticketInfo is null~");
log.info("====>failure of getting tokenInfo,please do some check~");
}
}
//生成微信权限验证的参数
Map<String, String> wechatParam= makeWXTicket(jsApiTicket,url);
return ResponseModel.success(wechatParam);
}
//获取accessToken
private JSONObject getAccessToken(){
//String accessTokenUrl = https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
String requestUrl = weChatBo.getAccessTokenUrl().replace("APPID",weChatBo.getAppId()).replace("APPSECRET",weChatBo.getAppSecret());
log.info("getAccessToken.requestUrl====>"+requestUrl);
JSONObject result = JSONObject.fromObject(HttpUtils.doGet(requestUrl));
return result ;
}
//获取ticket
private JSONObject getJsApiTicket(){
//String apiTicketUrl = https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
String requestUrl = weChatBo.getApiTicketUrl().replace("ACCESS_TOKEN", accessToken);
log.info("getJsApiTicket.requestUrl====>"+requestUrl);
JSONObject result = JSONObject.fromObject(HttpUtils.doGet(requestUrl));
return result;
}
//生成微信权限验证的参数
public Map<String, String> makeWXTicket(String jsApiTicket, String url) {
Map<String, String> ret = new HashMap<String, String>();
String nonceStr = createNonceStr();
String timestamp = createTimestamp();
String string1;
String signature = "";
//注意这里参数名必须全部小写,且必须有序
string1 = "jsapi_ticket=" + jsApiTicket +
"&noncestr=" + nonceStr +
"×tamp=" + timestamp +
"&url=" + url;
log.info("String1=====>"+string1);
try
{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(string1.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
log.info("signature=====>"+signature);
}
catch (NoSuchAlgorithmException e)
{
log.error("WeChatController.makeWXTicket=====Start");
log.error(e.getMessage(),e);
log.error("WeChatController.makeWXTicket=====End");
}
catch (UnsupportedEncodingException e)
{
log.error("WeChatController.makeWXTicket=====Start");
log.error(e.getMessage(),e);
log.error("WeChatController.makeWXTicket=====End");
}
ret.put("url", url);
ret.put("jsapi_ticket", jsApiTicket);
ret.put("nonceStr", nonceStr);
ret.put("timestamp", timestamp);
ret.put("signature", signature);
ret.put("appid", weChatBo.getAppId());
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 createNonceStr() {
return UUID.randomUUID().toString();
}
//生成时间戳
private static String createTimestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
}