微信二次分享(标题、摘要、缩略图)

目的

公司需要实现一个手机在微信分享的网页带标题、摘要和缩略图的功能,查看了微信开发文档,是一个二次分享比较简单的功能,下面开始写代码吧

要求

我们开始写代码之前需要做一些准备:

  1. 某云服务器一台
  2. 备案域名(将域名解析到服务器ip)
  3. 微信公众号

准备工作

  1. 将我们的域名添加到微信公众号的JS安全域名中,这里需要注意一下,你设置的域名带www就填上,不带就不填
    公众号设置
  2. 设置AppSecret

将公众号的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+"&timestamp="+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) {
	});

结束

至此,微信二次分享已经结束了,感兴趣的可以自己动手试一下,有时候自己认为简单的东西并不是那么简单的,我这次就因为白名单的问题折腾了好几个小时,最后发现因为白名单没有设置,给自己蠢哭了要!
在这里插入图片描述

总结

不轻视任何代码和问题、不轻视任何人、细节决定成败 真正的大师永远怀着一颗学徒的心

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值