根据开发文档,首先看一下前端需要什么数据:
var currentUrl = window.location.href;
//微信分享功能实现
$.ajax({//调取接口返回验证码
type:"get",
url:"http://域名/接口名",
data:{'url':currentUrl},
success:function(res){
console.log(res)
if(res.success==true){
//分享链接
var imgUrl = '图片地址';
var lineLink = 'http://域名/文件名/';
var descContent = "分享展示内容";
var shareTitle = '分享标题';
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: 'wxdbcbd7962d016d0b', // 必填,公众号的唯一标识
timestamp: res.data.timestamp, // 必填,生成签名的时间戳
nonceStr: res.data.nonceStr, // 必填,生成签名的随机串
signature: res.data.signature,// 必填,签名,见附录1
jsApiList: [
'checkJsApi',
'onMenuShareAppMessage',
'onMenuShareTimeline',
'onMenuShareQQ'
] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
wx.ready(function(){
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
wx.checkJsApi({
jsApiList: ['onMenuShareAppMessage','onMenuShareTimeline','onMenuShareQQ'], // 需要检测的JS接口列表,所有JS接口列表见附录2,
success: function(res) {
// console.log(res);
// 以键值对的形式返回,可用的api值true,不可用为false
// 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
}
});
wx.onMenuShareAppMessage({
title: shareTitle, // 分享标题
desc: descContent, // 分享描述
link: lineLink, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: imgUrl, // 分享图标
type: '', // 分享类型,music、video或link,不填默认为link
dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
success: function () {
// 用户确认分享后执行的回调函数
//alert('success')
},
cancel: function () {
// 用户取消分享后执行的回调函数
// alert('false');
}
});
wx.onMenuShareTimeline({
title: shareTitle, // 分享标题
link: lineLink, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: imgUrl, // 分享图标
success: function () {
// 用户确认分享后执行的回调函数
// alert('success')
},
cancel: function () {
// 用户取消分享后执行的回调函数
// alert('false');
}
});
wx.onMenuShareQQ({
title: shareTitle, // 分享标题
desc: descContent, // 分享描述
link: lineLink, // 分享链接
imgUrl: imgUrl, // 分享图标
success: function () {
// 用户确认分享后执行的回调函数
},
cancel: function () {
// 用户取消分享后执行的回调函数
}
});
});
wx.error(function(res){
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
//alert(res);
return;
});
}
else {
// alert(res.message)
return
}
},
error:function(){
console.log("error");
}
});
通过上面的js可以看到后台需要提供三个参数
一、timestamp
根据文档得知,要想得到timestamp需要得到jsapi_ticket,而要想得到jsapi_ticket需要得到aaccess_token。
1、获取access_token
access_token有效期为7200秒,重复获取将导致上次获取的access_token失效,所以问题是如何定时获取并且把得到的数据放在本地。
1)新建一个AccessToken实体类
public class AccessToken {
private String accessToken;
private int expiresin;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public int getExpiresin() {
return expiresin;
}
public void setExpiresin(int expiresin) {
this.expiresin = expiresin;
}
}
2)写一个servlet定时获取access_token,并配置在web.xml中
AccessTokenServlet:
@WebServlet(name = "accessTokenServlet")
public class AccessTokenServlet extends HttpServlet{
private static final long serialVersionUID = L;
private static Logger log = LoggerFactory.getLogger(WxCommonUtil.class);
@Override
public void init() throws ServletException {
TokenThread.appId = getInitParameter("appid"); //获取servlet初始参数appid和appsecret
TokenThread.appSecret = getInitParameter("appsecret");
log.info("weixin api appid:{}", TokenThread.appId);
log.info("weixin api appsecret:{}", TokenThread.appSecret);
if ("".equals(TokenThread.appId) || "".equals(TokenThread.appSecret)) {
log.error("appid and appsecret configuration error, please check carefully.");
} else {
// 启动定时获取access_token的线程
new Thread(new TokenThread()).start();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
web.xml配置
<servlet>
<servlet-name>accessTokenServlet</servlet-name>
<servlet-class>com.loan.servlet.AccessTokenServlet</servlet-class>
<init-param>
<param-name>appid</param-name>
<param-value>你的appid</param-value>
</init-param>
<init-param>
<param-name>appsecret</param-name>
<param-value>你的appsecret</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
3)TokenThread类
public class TokenThread implements Runnable {
private static Logger log = LoggerFactory.getLogger(TokenThread.class);
public static String appId = "";
public static String appSecret= "";
public static AccessToken accessToken = null;
@Override
public void run() {
while (true) {
try {
accessToken = WxCommonUtil.getToken(appId,appSecret);
if (null != accessToken) {
System.out.println(accessToken.getAccessToken());
Thread.sleep( * ); //获取到access_token 休眠7000秒
} else {
Thread.sleep( * ); //获取的access_token为空 休眠3秒
}
} catch (Exception e) {
System.out.println("发生异常:" + e.getMessage());
e.printStackTrace();
try {
Thread.sleep( * ); //发生异常休眠1秒
} catch (Exception e1) {
}
}
}
}
}
4)WxCommonUtil工具类
public class WxCommonUtil {
private static Logger log = LoggerFactory.getLogger(WxCommonUtil.class);
public final static String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=你的appid&secret=你的secret";
public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
JSONObject jsonObject = null;
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(ssf);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
// 当outputStr不为null时向输出流写数据
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
// 释放资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
conn.disconnect();
jsonObject = JSONObject.fromObject(buffer.toString());
} catch (ConnectException ce) {
log.error("连接超时:{}", ce);
} catch (Exception e) {
log.error("https请求异常:{}", e);
}
return jsonObject;
}
这样accessToken可以通过TokenThread.accessToken.getAccessToken()获取。
2、获取jsapi_ticket
/**
* 获取jsapi_ticket
*
* @param access_token
* @return
*/
public static String getTicket(String access_token) {
String ticket = null;
String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+ access_token +"&type=jsapi";//这个url链接和参数不能变
try {
URL urlGet = new URL(url);
HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
http.setRequestMethod("GET"); // 必须是get方式请求
http.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
http.setDoOutput(true);
http.setDoInput(true);
System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒
System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒
http.connect();
InputStream is = http.getInputStream();
int size = is.available();
byte[] jsonBytes = new byte[size];
is.read(jsonBytes);
String message = new String(jsonBytes, "UTF-8");
JSONObject demoJson = JSONObject.fromObject(message);
System.out.println("JSON字符串:"+demoJson);
ticket = demoJson.getString("ticket");
is.close();
} catch (Exception e) {
e.printStackTrace();
}
return ticket;
}
3、获取url
//这里URL要从前端动态获取,不能写死
String url=request.getParameter("url");
4、拼接字符串并进行sha1加密
String str = "jsapi_ticket="+jsapi_ticket+"&noncestr="+noncestr+"×tamp="+timestamp+"&url="+url;
/**
* sha1的加密算法
*
* @param decript
* @return
*/
public static String SHA1(String decript) {
String signature="";
try
{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(decript.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
return signature;
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
return "";
}
/**
* 进制转换
* @param hash
* @return
*/
public 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;
}
这样一系列步骤之后就可以得到signature。
二、timestamp
String timestamp = String.valueOf(System.currentTimeMillis() / );
三、nonceStr
这样就能得到三个参数啦。
最后是总的controller:
@RequestMapping(value = "/sign",method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"})
@ResponseBody
public ObjectOutput sign(HttpServletRequest request, HttpServletResponse response, HttpSession session){
Hashtable hashtable = new Hashtable();
//、获取accessToken
String accessToken= TokenThread.accessToken.getAccessToken();
//、获取Ticket
String jsapi_ticket=WxCommonUtil.getTicket(accessToken);
//、时间戳和随机字符串
String noncestr = UUID.randomUUID().toString().replace("-", "").substring(, );
String timestamp = String.valueOf(System.currentTimeMillis() / );
//、获取url
String url=request.getParameter("url");
//、讲参数排序并拼接字符串
String str = "jsapi_ticket="+jsapi_ticket+"&noncestr="+noncestr+"×tamp="+timestamp+"&url="+url;
//、将字符串进行sha1加密
String signature =WxCommonUtil.SHA1(str);
hashtable.put("signature",signature);
hashtable.put("timestamp",timestamp);
hashtable.put("nonceStr",noncestr);
hashtable.put("url",url);
output = Format.output(true, Constant.Success, hashtable, "");
return output;
}