公司需要实现一个手机在微信分享的网页带标题、摘要和缩略图的功能,查看了微信开发文档,是一个二次分享比较简单的功能,下面开始写代码吧
我们开始写代码之前需要做一些准备:
将公众号的AppID和AppSecret都记下来,一会要用到
3. 将我们的服务器ip地址添加到微信IP白名单中,获取token必须做的
因为微信的access_token和jsapi_ticket都有7200秒的有效期并且每天有限制获取次数,所以应该缓存起来,这里我使用的是redis缓存,大家也可以用其他缓存。
微信二次分享代码片
package com.ether.library.web.utils;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/***
* 微信二次分享工具类
* @author: 狮子头
*/
public class WeChartShareUtils {
/**
* 缓存有效时间(秒)
*/
private final static int CACHE_SECOND = 7200;
final static String JSAPI_TICKET = "jsapiTicket";
final static String ACCESS_TOKEN = "accessToken";
/**
* 获取微信jsapi_ticket
*/
public static String getJsapiTicket() {
// 先检查redis中jsapi_ticket是否存在
String jsapiTicket = (String) CusJedisUtils.getObject(JSAPI_TICKET);
if(!CusStringUtils.isNotNull(jsapiTicket)){
String accessToken = getAccessToken();
// 获取jsapi_ticket并保存到redis中
Map<String,Object> map = WeChartUtils.getJsapiTicket(accessToken);
jsapiTicket = (String) map.get(JSAPI_TICKET);
CusJedisUtils.setObject(JSAPI_TICKET, jsapiTicket, CACHE_SECOND);
}
return jsapiTicket;
}
/**
* 获取微信accessToken
*/
public static String getAccessToken() {
// 先获取accessToken在redis中是否存在
String accessToken = (String) CusJedisUtils.getObject(ACCESS_TOKEN);
if (!CusStringUtils.isNotNull(accessToken)) {
// 获取accessToken并保存到redis中
Map<String,Object> map = WeChartUtils.getAccessTokenByWechat();
if (map.get(ACCESS_TOKEN) != null) {
accessToken = (String) map.get(ACCESS_TOKEN);
CusJedisUtils.setObject(ACCESS_TOKEN, accessToken, CACHE_SECOND);
}
}
return accessToken;
}
/**
* 获取微信分享所需参数map
*/
public static Map<String,String> getWeChartShareMap(String url){
Map<String, String> map = new HashMap<>();
//1、获取Ticket
String jsapi_ticket = getJsapiTicket();
//2、时间戳和随机字符串
String noncestr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);//随机字符串
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);//时间戳
System.out.println("jsapi_ticket:"+jsapi_ticket+"\n时间戳:"+timestamp+"\n随机字符串:"+noncestr);
//3、将参数排序并拼接字符串
String str = "jsapi_ticket="+jsapi_ticket+"&noncestr="+noncestr+"×tamp="+timestamp+"&url="+url;
//4、将字符串进行sha1加密
String sign = WeChartUtils.SHA1(str);
map.put("appId", WeChartUtils.WEIXIN_APPID);
map.put("nonceStr", noncestr);
map.put("timestamp", timestamp);
map.put("signature", sign);
return map;
}
}
二次代码片
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sf.json.JSONObject;
/**
* 公众平台通用接口工具类
* @author: 狮子头
*/
public class WeChartUtils {
// 日志对象
protected static Logger logger = LoggerFactory.getLogger(WeChartUtils.class);
public final static String WEIXIN_APPID = "你的APPID";
public final static String WEIXIN_APPSECRET = "你的AppSecret";
// 获取access_token的接口地址(GET) 限200(次/天)
public static String access_Token_Url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
//获取jsticket
public static String get_jsticket = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=accessToken&type=jsapi";
/**
* 发起https请求并获取结果
* @param requestUrl 请求地址
*/
public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {
JSONObject jsonObject = null;
StringBuffer buffer = new StringBuffer();
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 httpUrlConn = (HttpsURLConnection) url.openConnection();
httpUrlConn.setSSLSocketFactory(ssf);
httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
// 设置请求方式(GET/POST)
httpUrlConn.setRequestMethod(requestMethod);
if ("GET".equalsIgnoreCase(requestMethod))
httpUrlConn.connect();
// 当有数据需要提交时
if (null != outputStr) {
OutputStream outputStream = httpUrlConn.getOutputStream();
// 注意编码格式,防止中文乱码
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 将返回的输入流转换成字符串
InputStream inputStream = httpUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
// 释放资源
inputStream.close();
inputStream = null;
httpUrlConn.disconnect();
jsonObject = JSONObject.fromObject(buffer.toString());
} catch (ConnectException ce) {
logger.error("WeChartUtils https server connection timed out....."+ce.getMessage());
} catch (Exception e) {
logger.error("WeChartUtils https request error:{}", e.getMessage());
}
return jsonObject;
}
/**
* 加密
*/
public static String SHA1(String decript) {
try {
MessageDigest digest = java.security.MessageDigest.getInstance("SHA-1");
digest.update(decript.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
// 字节数组转换为 十六进制 数
for (int i = 0; i < messageDigest.length; i++) {
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
logger.error("WeChartUtils SHA1 error:"+e.getMessage());
}
return "";
}
/**
* 通过微信获取token
*/
public static Map<String,Object> getAccessTokenByWechat(){
String requestUrl = access_Token_Url.replace("APPID", WEIXIN_APPID).replace("APPSECRET", WEIXIN_APPSECRET);
JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
Map<String,Object> map = new HashMap<>();
// 如果请求成功
if (null != jsonObject) {
try {
map.put(WeChartShareUtils.ACCESS_TOKEN, jsonObject.getString("access_token"));
map.put("expires_in", jsonObject.getInt("expires_in"));
logger.info("获取access_token成功,有效时长{}秒 token:{}", map.get("expires_in") ,map.get(WeChartShareUtils.ACCESS_TOKEN));
} catch (Error e) {
// 获取token失败
logger.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"),
jsonObject.getString("errmsg"));
}
}
return map;
}
/**
* 通过微信获取的ticket
*/
public static Map<String,Object> getJsapiTicket(String token){
String requestUrl = get_jsticket.replace(WeChartShareUtils.ACCESS_TOKEN, token);
JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
Map<String,Object> ticketMap = new HashMap<>();
// 如果请求成功
if (null != jsonObject) {
try {
ticketMap.put("errcode", jsonObject.getInt("errcode"));
ticketMap.put("errmsg", jsonObject.getString("errmsg"));
ticketMap.put("expires_in", jsonObject.getInt("expires_in"));
ticketMap.put(WeChartShareUtils.JSAPI_TICKET, jsonObject.getString("ticket"));
logger.info("获取jsapi_ticket成功,有效时长{}秒 token:{}", ticketMap.get("expires_in"), ticketMap.get(WeChartShareUtils.JSAPI_TICKET));
} catch (Error e) {
// 获取token失败
logger.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"),
jsonObject.getString("errmsg"));
}
}
return ticketMap;
}
}
package com.ether.library.web.utils;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
/**
* 信任管理器
* @author 狮子头
*/
public class MyX509TrustManager implements X509TrustManager {
// 检查客户端证书
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
// 检查服务器端证书
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
// 返回受信任的X509证书数组
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
controller请求
/**
* 获取微信二次分享参数
*/
@ResponseBody
@RequestMapping(value = "getWeChartMessage")
public Map<String, String> getWeChartMessage(HttpServletRequest request, HttpServletResponse response
, @RequestBody Map<String,String> map) {
//外部传入url获取url url需要是微信中打开的url否则会报错。
String url = map.get("url");
//需要转换解码url
try {
url = java.net.URLDecoder.decode(url,"UTF-8");
} catch (UnsupportedEncodingException e) {
logger.error("getWeChartMessage decode error:"+e.getMessage());
return null;
}
return WeChartShareUtils.getWeChartShareMap(url);
}
JS部分
var postData = {
"url" : window.location.href
}
//获取微信分享的各个参数
//这是使用的是我自己封装的ajax 大家正常写就行
$.hxAjax("/getWeChartMessage", postData, function(data) {
wx.config({
debug : true, //调试阶段建议开启
appId : data.appId,//APPID
timestamp : data.timestamp,//上面main方法中拿到的时间戳timestamp
nonceStr : data.nonceStr,//上面main方法中拿到的随机数nonceStr
signature : data.signature,//上面main方法中拿到的签名signature
//需要调用的方法接口
jsApiList : [ 'onMenuShareTimeline', 'onMenuShareAppMessage',
'onMenuShareWeibo', 'onMenuShareQQ', 'onMenuShareQZone' ]
});
wx.ready(function(){
//alert("我已经进来了");
wx.onMenuShareTimeline({
title: weChartShareTitle, // 分享标题
link: window.location.href, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: shareImg, // 分享图标
success: function () {
// alert("成功")
// 用户点击了分享后执行的回调函数
}
});
wx.onMenuShareAppMessage({
title: weChartShareTitle, // 分享标题
desc: weChartShareTitle + "-" + $("#name").val(), // 分享描述
link: window.location.href, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: "", // 分享图标
type: shareImg, // 分享类型,music、video或link,不填默认为link
dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
success: function () {
// alert("成功")
// 用户点击了分享后执行的回调函数
}
});
wx.onMenuShareQQ({
title: weChartShareTitle, // 分享标题
desc: weChartShareTitle + "-" + $("#name").val(), // 分享描述
link: window.location.href, // 分享链接
imgUrl: shareImg, // 分享图标
success: function () {
// alert("成功")
// 用户确认分享后执行的回调函数
},
cancel: function () {
// alert("失败")
// 用户取消分享后执行的回调函数
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
}
});
});
}, function(data) {
});
至此,微信二次分享已经结束了,感兴趣的可以自己动手试一下,有时候自己认为简单的东西并不是那么简单的,我这次就因为白名单的问题折腾了好几个小时,最后发现因为白名单没有设置,给自己蠢哭了要!
不轻视任何代码和问题、不轻视任何人、细节决定成败 真正的大师永远怀着一颗学徒的心