微信分享带图文(JAVA)
一、绑定JS安全域名,引入分享所需的JS文件
绑定js安全域有认证过的微信公众号,然后再“公众号设置”的“功能设置”里填写“JS接口安全域名”,填写前必须在对应的域名服务器的根目录(可访问目录)放置官方txt的文件。配置前最好看一遍官方文档。
<script type="text/javascript" src="https://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<script type="text/javascript" src="${theme_dir}/wx/js/share.js"></script>
这里我把其余的操作放入了share.js。
二、参数获取
根据官方文档可知:
我们需要的参数:appId,timestamp,nonceStr,signature,jsApiList
appId可以直接在公众号的基本配置里面找到;
jsApiList即为调用的微信公众号的接口。需要注意的是:
wx.onMenuShareTimeline(分享到朋友圈)、wx.onMenuShareAppMessage(分享给朋友)、
wx.onMenuShareQQ(分享给好友)、wx.onMenuShareQZone(分享到QQ空间) 这四个接口即将废弃。
客户端6.7.2及JSSDK 1.4.0以上版本支持的 :
wx.updateAppMessageShareData(分享给好友或朋友)、wx.updateTimelineShareData(分享到朋友圈或QQ空间)
这两个接口用来代替。(但是我这只能用旧的,而且手机端不生效,求大神解决)
timestamp、nonceStr和signature都是后台生成的这里有官方的代码Java的只需要把sign.java放入即可《下载链接》
public static Map<String, String> sign(String jsapi_ticket, String url) {
Map<String, String> ret = new HashMap<String, String>();
//String nonce_str = create_nonce_str();
//String timestamp = create_timestamp();
String nonce_str = create_nonce_str();
String timestamp = create_timestamp();
String string1;
String signature = "";
//注意这里参数名必须全部小写,且必须有序
string1 = "jsapi_ticket=" + jsapi_ticket +
"&noncestr=" + nonce_str +
"×tamp=" + timestamp +
"&url=" + url;
//System.out.println(string1);
try
{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(string1.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
ret.put("url", url);
ret.put("jsapi_ticket", jsapi_ticket);
ret.put("nonceStr", nonce_str);
ret.put("timestamp", timestamp);
ret.put("signature", signature);
return ret;
}
这是sign.java的部分代码,里面有上面三个参数的生成策略,需要传入参数jsapi_ticket和url,url就是要分享网站的网址,一般是前台传入的当前网址,jsapi_ticket就是需要通过
https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+newToken+"&type=jsapi
这个网址请求得到,这里需要获得access_token,而access_token需要通过
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+appId+"&secret="+appSecret
这个网址请求得到,这里需要获得appid和secret,这两个参数都可以从微信公众号的基本配置里看到,secret需要开启一下。
参数都有了就可以在分享请求时发送给参数给接口完成分享请求。
特别注意:
这里请求获取的的access_token和jsapi_ticket都有时间限制,一般来说为2个小时,而且请求也是有次数限制的(2000次),所以说在一般情况下,要对请求到的access_token和jsapi_ticket进行储存,分享前对储存的值得有效时间进行判断,有效期内便可以直接取用。
Controller:
private static final Logger LOGGER = Logger.getLogger(TokenAction.class);
@Resource
private TokenStoreService tokenStoreService;
private String url;
//获取ticket
@Action("/wx/open/userQudao/signature")
public void signature() throws Exception{
String newToken;
String newTicket;
//计算access_token
TokenStore tokenStore=tokenStoreService.getTokenBybindingId(EnumToken.TOKEN.value());//获取数据库存储的token
if(tokenStore!=null){
Date d=tokenStore.getAddTime();//获取上次token添加时间
Long expiresIn=Long.parseLong(tokenStore.getExpiresIn());//获取有效时间
Long timeDifference = DateUtil.timeDifference(d);//计算时间差
if(expiresIn/60-timeDifference<10){
//如果有效时间超过有效时间(-10min误差)则重新获取并更新数据库
LOGGER.info("重新获取token并更新数据库");
newToken = tokenStoreService.getTokenUpdateSql(Global.getValue("wx_appId"),Global.getValue("wx_appSecret"),tokenStore);
}else{
newToken = tokenStore.getOauthToken();
LOGGER.info("获取数据库的token,不执行更新");
}
}else{
//获取token并写入数据库
LOGGER.info("执行Token添加");
newToken = tokenStoreService.getTokenUpdateSql(Global.getValue("wx_appId"),Global.getValue("wx_appSecret"),tokenStore);
}
//根据token获取ticket
TokenStore ticketStore=tokenStoreService.getTokenBybindingId(EnumToken.TICKET.value());//获取数据库存储的ticket
if(ticketStore!=null){
Date d=ticketStore.getAddTime();//获取上次ticket添加时间
Long expiresIn=Long.parseLong(ticketStore.getExpiresIn());//获取有效时间
Long timeDifference = DateUtil.timeDifference(d);//计算时间差
if(expiresIn/60-timeDifference<10){
//如果有效时间超过有效时间(-10min误差)则重新获取并更新数据库
LOGGER.info("重新获取ticket并更新数据库");
newTicket = tokenStoreService.getTicketUpdateSql(ticketStore,newToken);
}else{
newTicket = ticketStore.getOauthToken();
LOGGER.info("获取数据库的ticket,不执行更新");
}
}else{
newTicket = tokenStoreService.getTicketUpdateSql(ticketStore,newToken);
LOGGER.info("执行Ticket添加");
}
//调用微信公众号官方方法生成所需信息
Map<String, String> sign = Sign.sign(newTicket.toString(), this.getUrl());
//发送json
sign.put("appId", EnumToken.APPID.value());
printWebJson(JSON.toJSONString(sign));
LOGGER.info("json.为:"+JSON.toJSONString(sign));
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
service
/**
* 获取token并更新数据库(超时或者不存在时)
*/
public String getTokenUpdateSql(String appId, String appSecret,TokenStore tokenStore) {
//获取token
String getTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+appId+"&secret="+appSecret;
JSONObject newTokenStr = HttpUtil.doGet(getTokenUrl);
JSONObject newTokenJson = JSONObject.parseObject(newTokenStr.toJSONString());
String newToken = newTokenJson.getString("access_token");
String expiresIn=newTokenJson.getString("expires_in");
//写入数据库
TokenStore tokenStore2=new TokenStore();
tokenStore2.setBindingId(EnumToken.TOKEN.value());
tokenStore2.setAddTime(new Date());
tokenStore2.setExpiresIn(expiresIn);
tokenStore2.setOauthToken(newToken);
if(tokenStore!=null) {
tokenStore2.setId(tokenStore.getId());
this.refreshToken(tokenStore2);
//System.out.println("执行了Token修改");
} else {
this.save(tokenStore2);
//System.out.println("执行了Token添加");
}
return newToken;
}
/**
* 获取ticket并更新数据库(超时或者不存在时)
*/
public String getTicketUpdateSql(TokenStore ticketStore,String newToken) {
//获取token
String jsapiUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+newToken+"&type=jsapi";
JSONObject newTicketStr = HttpUtil.doGet(jsapiUrl);
JSONObject newTicketJson = JSONObject.parseObject(newTicketStr.toJSONString());
String newTicket = newTicketJson.getString("ticket");
String expiresIn = newTicketJson.getString("expires_in");
//写入数据库
TokenStore ticketStore2=new TokenStore();
ticketStore2.setBindingId(EnumToken.TICKET.value());
ticketStore2.setAddTime(new Date());
ticketStore2.setExpiresIn(expiresIn);
ticketStore2.setOauthToken(newTicket);
if(ticketStore!=null) {
ticketStore2.setId(ticketStore.getId());
this.refreshToken(ticketStore2);
//System.out.println("执行了Ticket修改");
} else {
this.save(ticketStore2);
//System.out.println("执行了Ticket添加");
}
return newTicket;
}
share.js
$(function(){
var imgUrl = ''; //分享的图片/小图标
var lineLink = window.location.href; //当前 页面 地址
var descContent = ''; //分享简介 小的 图文里面 的 文字部分
var shareTitle = ''; //标题 这里为 文章标题
var appid = '';
$.ajax({
type : 'POST',
url : "", //请求控制器地址
dataType : "json",
data:{
url:window.location.href
},
success : function(response){
var appId = response.appid; //微信公众号 appid 以下看后端备注
var timestamp = response.timestamp;
var noncestr = response.nonceStr;
var signature = response.signature;
console.log(response);
wx.config({
debug: false,//开启调试模式,调用的所有api的返回值会在客户端alert出来,所有的接口信息会在Console里显示
appId: appId,//微信id
timestamp: timestamp,//时间戳
nonceStr: noncestr,//随机字符串
signature: signature,//签名
jsApiList: [
'checkJsApi',
'onMenuShareAppMessage',
'onMenuShareTimeline',
'onMenuShareQQ',
'updateAppMessageShareData',
'updateTimelineShareData'
]//使用到的接口
});
wx.ready(function() {
// 判断当前客户端版本是否支持指定JS接口
wx.checkJsApi({
jsApiList: [
'onMenuShareAppMessage',
'onMenuShareTimeline',
'updateAppMessageShareData',
'updateTimelineShareData',
], // 需要检测的JS接口列表,所有JS接口列表见附录2,
success: function(res) {
console.log(JSON.stringify(res));
// 以键值对的形式返回,可用的api值true,不可用为false
// 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
}
});
// 新
// 自定义“分享给朋友”及“分享到QQ”按钮的分享内容(1.4.0)
wx.updateAppMessageShareData({
title: shareTitle, // 分享标题
link: lineLink, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: imgUrl, // 分享图标
desc: descContent, // 分享描述
success: function (res) {
// 用户确认分享后执行的回调函数
// alert("Xxinok"+JSON.stringify(res));
},
cancel: function (res) {
// 用户取消分享后执行的回调函数
// alert("X分享取消 "+JSON.stringify(res));
},
fail: function (res) {
// alert("X分享失败 "+JSON.stringify(res));
}
});
// 自定义“分享到朋友圈”及“分享到QQ空间”按钮的分享内容(1.4.0)
wx.updateTimelineShareData({
title: shareTitle, // 分享标题
desc: descContent, // 分享描述
link: lineLink, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: imgUrl, // 分享图标
type: '', // 分享类型,music、video或link,不填默认为link
dataUrl:'' , // 如果type是music或video,则要提供数据链接,默认为空
success: function (res) {
// 用户确认分享后执行的回调函数
},
cancel: function (res) {
// 用户取消分享后执行的回调函数
},
fail: function (res) {
}
});
// 旧
// 自定义“分享给朋友”
wx.onMenuShareAppMessage({
title: shareTitle, // 分享标题
link: lineLink, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: imgUrl, // 分享图标
desc: descContent, // 分享描述
success: function (res) {
// 用户确认分享后执行的回调函数
},
cancel: function () {
// 用户取消分享后执行的回调函数
},
fail: function () {
}
});
// 自定义“分享到朋友圈”
wx.onMenuShareTimeline({
title: shareTitle, // 分享标题
desc: descContent, // 分享描述
link: lineLink, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: imgUrl, // 分享图标
type: '', // 分享类型,music、video或link,不填默认为link
dataUrl:'' , // 如果type是music或video,则要提供数据链接,默认为空
success: function (res) {
// 用户确认分享后执行的回调函数
//alert("ok"+JSON.stringify(res));
},
cancel: function (res) {
// 用户取消分享后执行的回调函数
//alert("分享取消 "+JSON.stringify(res));
},
fail: function (res) {
}
});
//-------------分享到QQ
wx.onMenuShareQQ({
title: shareTitle, // 分享标题
desc: descContent, // 分享描述
link: lineLink, // 分享链接
imgUrl: imgUrl, // 分享图标
success: function (res) {
// 用户确认分享后执行的回调函数
},
cancel: function (res) {
// 用户取消分享后执行的回调函数
},
fail: function (res) {
}
});
//-------------分享到QQ空间
wx.onMenuShareQZone({
title: shareTitle, // 分享标题
desc: descContent, // 分享描述
link: lineLink, // 分享链接
imgUrl: imgUrl, // 分享图标
success: function () {
// 用户确认分享后执行的回调函数
},
cancel: function () {
// 用户取消分享后执行的回调函数
}
});
});
},
error:function(response){
//alert(response.errMsg)
}
});
})