首页 > 技术文章 > java开发-微信二次分享

kingyifan 2018-12-21 13:16 原文

今天踩了一个很大的坑。。我发现微信坑真多啊。微信做的东西真的不屑于评论啊。刚开始坑公众号开发,然后坑微信支付开发,然后坑小程序开发。我真的走不出来了。。。。关键还没办法。

今天给大家分享一下app的二次分享问题吧。

file

是不是挺好玩的。这个时候就说了为什么变成这个样子,因为他分享H5没有写微信的二次分享。

二次分享操作流程:

  1. 打开某app然后分享到朋友某一个篇文章。
  2. 然后朋友打开,觉得写得不错,然后呢也分享了,然后就出现了下图的现象。。
  3. 所有参数都暴露了不说,关键是难看啊。你看上图。

画个图图告诉你什么是二次分享:

file

代码流程炒鸡简单。我给你们也画一个流程图。

file

老规矩:

点我快速进入分享sdk官方文档

直接上代码吧。代码有些糙 不喜欢勿喷。

	前段需要引入的js
		<!--微信分享js  -->
		<script src="http://res2.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
		<script src="static/js/wx_share.js"></script

		前端的代码:我用的el表达式,jsp页面。别的可根据自己进行修改。
		<input type="hidden"value="${shareDate.nonceStr }" id="nonceStr">
		<input type="hidden"value="${shareDate.timestamp }" id="timestamp">
		<input type="hidden"value="${shareDate.signature }" id="signature">
		<input type="hidden"value="${shareDate.appid }" id="appid">
		<input type="hidden"value="${shareDate.share_title }" id="share_title">
		<input type="hidden"value="${shareDate.share_detail }" id="share_detail">
		<input type="hidden"value="${shareDate.share_image }" id="share_image">
		<input type="hidden"value="${shareDate.share_url }" id="share_url">	
		
		自己的封装的wx_share.js
		$(function() {
			var timestamp = $("#timestamp").val();
			var nonceStr = $("#nonceStr").val();//随机串
			var signature = $("#signature").val();//签名
			var appid = $("#appid").val();
			var title = $("#share_title").val();
			var desc = $("#share_detail").val();
			var link = $("#share_url").val();
			var imgUrl = $("#share_image").val();
			wx.config({
				debug : true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
				appId : appid, // 必填,公众号的唯一标识
				timestamp : timestamp, // 必填,生成签名的时间戳
				nonceStr : nonceStr, // 必填,生成签名的随机串
				signature : signature,// 必填,签名,见附录1
				jsApiList : [ 'onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo' ]
			// 必填,需要使用的JS接口列表,所有JS接口列表见附录2
			});

			wx.ready(function(){
				wx.onMenuShareTimeline({
						title: title,
					link: link,
					imgUrl: imgUrl,
					success: function () { 
						// 用户确认分享后执行的回调函数
						 alert('分享到朋友圈成功');
					},
					cancel: function () { 
						// 用户取消分享后执行的回调函数
						 alert('你没有分享到朋友圈');
					}
				});
				wx.onMenuShareAppMessage({
					  title: title,
					  desc: desc,
					  link: link,
					  imgUrl: imgUrl,
					  success: function (res) {
						  alert('分享给朋友成功');
					  },
					  cancel: function (res) {
						alert('你没有分享给朋友');
					  },
				});
				wx.onMenuShareQQ({
						title: title,
					desc: desc,
					link: link,
					imgUrl: imgUrl,
					success: function () { 
					   // 用户确认分享后执行的回调函数
						alert('分享给朋友成功');
					},
					cancel: function () { 
					   // 用户取消分享后执行的回调函数
					}
				});
				wx.onMenuShareWeibo({
						title: title,
					desc: desc,
					link: link,
					imgUrl: imgUrl,
					success: function () { 
					   // 用户确认分享后执行的回调函数
						alert('分享给朋友成功');
					},
					cancel: function () { 
						// 用户取消分享后执行的回调函数
					}
				});
				wx.onMenuShareQZone({
						title: title,
					desc: desc,
					link: link,
					imgUrl: imgUrl,
					success: function () { 
					   // 用户确认分享后执行的回调函数
						alert('分享给朋友成功');
					},
					cancel: function () { 
						// 用户取消分享后执行的回调函数
					}
				});
			});
		});
		
		
		 
/**
	 * 分享数据test
	 * 
	 * @return
	 * @throws Exception
	 */
	@RequestMapping(value = "/share_date", method = RequestMethod.GET)
	public ModelAndView share_date() throws Exception {
		ModelAndView modelAndView = this.getModelAndView();
		PageData pd = new PageData();
		pd = this.getPageData();
		try {
				PageData shareDate = commonService.wx_share_date("test", "hehe", "xxxx.img", "xxx");
				modelAndView.addObject("shareDate", shareDate);				
		     	modelAndView.setViewName("xx/xxx/xxx");

		} catch (Exception e) {
			e.printStackTrace();
			logBefore(logger, e.toString());
			throw e;
		}
		return modelAndView;
	}
	/**
	 * 封装二次分享数据返回前台
	 * 
	 * @param share_title  分享标题
	 * @param share_detail 分享描述
	 * @param share_image  分享图片
	 * @param share_url    分享打开连接
	 * @return
	 * @throws Exception
	 */
	public PageData wx_share_date(String share_title, String share_detail, String share_image, String share_url)
			throws Exception {
		
		//微信所需要的数据
		PageData wxDate = Sign.retSign(wechatShareUtil.getJsapiTicket(), share_url);
		wxDate.put("appid", WeChatUtil.WEIXIN_APPID);
		
		//分享所需要数据
		wxDate.put("share_title", share_title);
		wxDate.put("share_detail", share_detail);
		wxDate.put("share_image", share_image);
		wxDate.put("share_url", share_url);
		return wxDate; 
	}
	
	package cn.cnbuilder.util;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import cn.cnbuilder.dao.redis.RedisDao;
import cn.cnbuilder.entity.AccessToken;
import cn.cnbuilder.entity.TicketJson;
import cn.cnbuilder.util.weixin.WeChatUtil;

import net.sf.json.JSONException;
import net.sf.json.JSONObject;

@Component
public class WechatShareUtil {

	@Resource(name = "redisDaoImpl")
	private RedisDao redisDaoImpl;

	private static Logger log = LoggerFactory.getLogger(WeChatUtil.class);

	/**
	 * 获取微信jsapi_ticket
	 * 
	 * 
	 * @param pd
	 * @throws Exception
	 */
	public String getJsapiTicket() {

		// 先获取jsapi_ticket在redis中是否存在
		String gzh_jsapiTicket = redisDaoImpl.get("jsapiTicket");
		if (Tools.isEmpty(gzh_jsapiTicket)) {
			String accessToken = getAccessToken();
			// 获取jsapi_ticket并保存到redis中
			TicketJson ticketJson = getJsapiTicket(accessToken);
			gzh_jsapiTicket = ticketJson.getTicket();
			if (Tools.notEmpty(gzh_jsapiTicket)) {
				redisDaoImpl.addString("jsapiTicket", gzh_jsapiTicket, ticketJson.getExpires_in());
			}
		}
		return gzh_jsapiTicket;
	}

	/**
	 * 获取微信accessToken
	 * 
	 * @param pd
	 * @throws Exception
	 */
	public String getAccessToken() throws JSONException {
		// 先获取accessToken在redis中是否存在
		String gzh_accessToken = redisDaoImpl.get("accessToken");
		if (Tools.isEmpty(gzh_accessToken)) {
			// 获取accessToken并保存到redis中
			AccessToken accessToken = getAccessTokenByWechat();
			if (accessToken != null) {
				gzh_accessToken = accessToken.getToken();
				redisDaoImpl.addString("accessToken", gzh_accessToken, accessToken.getExpiresIn());
			}
		}
		return gzh_accessToken;
	}

	/**
	 * @Description: 通过微信获取token
	 */
	public AccessToken getAccessTokenByWechat() throws JSONException {
		String requestUrl = WeChatUtil.access_Token_Url.replace("APPID", WeChatUtil.WEIXIN_APPID).replace("APPSECRET",
				WeChatUtil.WEIXIN_APPSECRET);
		JSONObject jsonObject = WeChatUtil.httpRequest(requestUrl, "GET", null);
		AccessToken accessToken = new AccessToken();
		// 如果请求成功
		if (null != jsonObject) {
			try {
				accessToken.setToken(jsonObject.getString("access_token"));
				accessToken.setExpiresIn(jsonObject.getInt("expires_in"));
				log.info("获取access_token成功,有效时长{}秒 token:{}", accessToken.getExpiresIn(), accessToken.getToken());
			} catch (JSONException e) {
				// 获取token失败
				log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"),
						jsonObject.getString("errmsg"));
			}
		}
		return accessToken;
	}

	/**
	 * @Description: 不通过判断,直接生成锌的token
	 */
	public TicketJson getJsapiTicket(String token) throws JSONException {
		String requestUrl = WeChatUtil.get_jsticket.replace("ACCESS_TOKEN", token);
		JSONObject jsonObject = WeChatUtil.httpRequest(requestUrl, "GET", null);
		TicketJson ticketJson = new TicketJson();
		// 如果请求成功
		if (null != jsonObject) {
			try {
				ticketJson.setErrcode(jsonObject.getInt("errcode"));
				ticketJson.setErrmsg(jsonObject.getString("errmsg"));
				ticketJson.setExpires_in(jsonObject.getInt("expires_in"));
				ticketJson.setTicket(jsonObject.getString("ticket"));
				log.info("获取jsapi_ticket成功,有效时长{}秒 token:{}", ticketJson.getExpires_in(), ticketJson.getTicket());
			} catch (JSONException e) {
				// 获取token失败
				log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"),
						jsonObject.getString("errmsg"));
			}
		}
		return ticketJson;
	}
}
package cn.cnbuilder.entity;

import java.io.Serializable;

/**
 * 微信通用接口凭证
 * 
 * @author KingYiFan
 * @date 2015-12-13
 */
public class AccessToken implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = -553323547802220881L;
	// 获取到的凭证
	private String token;
	// 凭证有效时间,单位:秒
	private int expiresIn;

	public String getToken() {
		return token;
	}

	public void setToken(String token) {
		this.token = token;
	}

	public int getExpiresIn() {
		return expiresIn;
	}

	public void setExpiresIn(int expiresIn) {
		this.expiresIn = expiresIn;
	}
}

package cn.cnbuilder.entity;

import java.io.Serializable;

/**
 * 微信通用接口凭证
 * 
 * @author KingYiFan
 * @date 2015-12-13
 */
public class TicketJson implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private int errcode;  
    private String errmsg;  
    private String ticket;  
    private int expires_in;  
    public int getErrcode() {  
        return errcode;  
    }  
    public void setErrcode(int errcode) {  
        this.errcode = errcode;  
    }  
    public String getErrmsg() {  
        return errmsg;  
    }  
    public void setErrmsg(String errmsg) {  
        this.errmsg = errmsg;  
    }  
    public String getTicket() {  
        return ticket;  
    }  
    public void setTicket(String ticket) {  
        this.ticket = ticket;  
    }  
    public int getExpires_in() {  
        return expires_in;  
    }  
    public void setExpires_in(int expires_in) {  
        this.expires_in = expires_in;  
    }
}

。。。wechatUtils

package cn.cnbuilder.util.weixin;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.URL;
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 javax.annotation.Resource;
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 cn.cnbuilder.dao.redis.RedisDao;
import cn.cnbuilder.entity.AccessToken;
import cn.cnbuilder.entity.TicketJson;
import cn.cnbuilder.util.MyX509TrustManager;
import cn.cnbuilder.util.PageData;
import cn.cnbuilder.util.Tools;
import cn.cnbuilder.util.wxpay.Sign;

import net.sf.json.JSONException;
import net.sf.json.JSONObject;

/**
 * 公众平台通用接口工具类
 */
public class WeChatUtil {
	
	@Resource(name = "redisDaoImpl")
	private RedisDao redisDaoImpl;
	
	private static Logger log = LoggerFactory.getLogger(WeChatUtil.class);

	 
	public static String WEIXIN_APPID = "xxxx";
	public static String WEIXIN_APPSECRET = "xxxx";

	// 获取access_token的接口地址(GET) 限200(次/天)
	public final static String access_Token_Url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
+ WEIXIN_APPID + "&secret=" + WEIXIN_APPSECRET;
	// 拉取用户信息(需scope为 snsapi_userinfo) ACCESS_TOKEN 是网页授权的ACCESS_TOKEN
	public final static String user_Info_Url_login = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID";

	// 获取用户基本信息(包括UnionID机制)
	public final static String user_Info_Url = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";

	// 用户同意授权,获取code
	public final static String scope_Code_Url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="
			+ WEIXIN_APPID
			+ "&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect";
	// 通过code换取网页授权access_token
	public final static String scope_OpenId_Url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="
			+ WEIXIN_APPID + "&secret=" + WEIXIN_APPSECRET + "&code=CODE&grant_type=authorization_code";
	// 由于access_token拥有较短的有效期,当access_token超时后,可以使用refresh_token进行刷新,refresh_token有效期为30天,当refresh_token失效之后,需要用户重新授权。
	public final static String refresh_token_Url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid="
			+ WEIXIN_APPID + "&grant_type=refresh_token&refresh_token=REFRESH_TOKEN";

	// 获取用户code值
	public final static String get_code_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="
			+ WEIXIN_APPID
			+ "&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";

	//获取jsticket
	public final static String get_jsticket = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";

	
	
	/**
	 * 通过code获取网页授权 和用户openid
	 * 
	 * @param code
	 * @return
	 */
	public static Map<String, Object> getOpenId(String code) {
		Map<String, Object> resMap = new HashMap<>();
		String openId = null;
		String accessToken = null;
		String refreshToken = null;
		String url = scope_OpenId_Url.replace("CODE", code);
		JSONObject jsonObject = httpRequest(url, "POST", null);
		log.info("WeChatUtil getOpenId=" + jsonObject);
		if (null != jsonObject) {
			if (!jsonObject.containsKey("errcode")) {
				openId = jsonObject.getString("openid");
				accessToken = jsonObject.getString("access_token");
				refreshToken = jsonObject.getString("refresh_token");
				resMap.put("openId", openId);
				resMap.put("accessToken", accessToken);
				resMap.put("refresh_token", refreshToken);
				return resMap;
			} else {
				int errorCode = jsonObject.getInt("errcode");
				String errorMsg = jsonObject.getString("errmsg");
				log.info("通过code换取网页授权失败errorCode:{" + errorCode + "},errmsg:{" + errorMsg + "}");
				System.out.println("通过code换取网页授权失败errorCode:{" + errorCode + "},errmsg:{" + errorMsg + "}");
			 
			}
		}
		return resMap;
		
	}

	/**
	 * 通过openId和accessToken获取当前用户的基本信息
	 * 
	 * @param openId
	 * @param accessToken
	 * @return
	 */
	public static JSONObject getUserInfo2(String openId, String accessToken) {
		String url = user_Info_Url.replace("OPENID", openId).replace("ACCESS_TOKEN", accessToken);
		JSONObject jsonObject = httpRequest(url, "POST", null);
		log.info("WeChatUtil getUserInfo=" + jsonObject);
		return jsonObject;
	}

	/**
	 * 通过appId和appSecretId获取accessToken
	 * 
	 * @date 2018年3月6日
	 * @return
	 */
	public static String getAccessToken() {
		String url = access_Token_Url.replace("WEIXIN_APPID", WEIXIN_APPID).replace("WEIXIN_APPSECRET",
				WEIXIN_APPSECRET);
		JSONObject jsonObject = httpRequest(url, "GET", null);
		log.info("WeChatUtil getAccessToken=" + jsonObject);
		return jsonObject.getString("access_token");
	}

	/**
	 * 获取用户code值
	 */
	public static String getCodeUrl(String redirect_uri) {
		String url = get_code_url.replace("REDIRECT_URI", redirect_uri);
		return url;

	}

	/**
	 * 刷新token有效期
	 * 
	 * @date 2018年3月6日
	 * @return
	 */
	public static String refreshToken(String REFRESH_TOKEN) {
		String url = refresh_token_Url.replace("REFRESH_TOKEN", REFRESH_TOKEN);
		JSONObject jsonObject = httpRequest(url, "GET", null);
		log.info("WeChatUtil refreshToken=" + jsonObject);
		return jsonObject.getString("access_token");
	}
	
	/**
	 * 获取jsticket
	 * 
	 * @date 2018年3月6日
	 * @return
	 */
	public static String getJsTicket(String accessToken) {
		String url = get_jsticket.replace("ACCESS_TOKEN", accessToken);
		JSONObject jsonObject = httpRequest(url, "GET", null);
		return jsonObject.getString("ticket");
	}
	
	/**
	 * URL编码(utf-8)
	 * 
	 * @param source
	 * @return
	 */
	public static String urlEncodeUTF8(String source) {
		String result = source;
		try {
			result = java.net.URLEncoder.encode(source, "utf-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			log.error("urlEncodeUTF8出现异常!\n" + e.getMessage());
		}
		return result;
	}

	public static String getWEIXIN_APPID() {
		return WEIXIN_APPID;
	}

	public static void setWEIXIN_APPID(String wEIXIN_APPID) {
		WEIXIN_APPID = wEIXIN_APPID;
	}

	public static String getWEIXIN_APPSECRET() {
		return WEIXIN_APPSECRET;
	}

	public static void setWEIXIN_APPSECRET(String wEIXIN_APPSECRET) {
		WEIXIN_APPSECRET = wEIXIN_APPSECRET;
	}

	/**
	 * 发起https请求并获取结果
	 * 
	 * @param requestUrl    请求地址
	 * @param requestMethod 请求方式(GET、POST)
	 * @param outputStr     提交的数据
	 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
	 */
	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) {
			ce.printStackTrace();
			log.error("Weixin server connection timed out.");
		} catch (Exception e) {
			e.printStackTrace();
			log.error("https request error:{}", e);
		}
		return jsonObject;
	}

	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 string1;
		String signature = "";

		// 注意这里参数名必须全部小写,且必须有序
		string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "&timestamp=" + 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;
	}

	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 create_nonce_str() {
		return UUID.randomUUID().toString();
	}

	private static String create_timestamp() {
		return Long.toString(System.currentTimeMillis() / 1000);
	}

}

加密

package cn.cnbuilder.util.wxpay;

import java.util.UUID;

import cn.cnbuilder.util.PageData;

import java.util.Map;
import java.util.Formatter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.io.UnsupportedEncodingException;  

public class Sign {
    public static void main(String[] args) {
        String jsapi_ticket = "jsapi_ticket";

        // 注意 URL 一定要动态获取,不能 hardcode
        String url = "http://baidu.com";
        Map<String, String> ret = retSign(jsapi_ticket, url);
        
    };

    public static PageData retSign(String jsapi_ticket, String url) {
    		PageData ret = new PageData();
        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;
        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("jsapi_ticket", jsapi_ticket);
        ret.put("nonceStr", nonce_str);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);

        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 create_nonce_str() {
        return UUID.randomUUID().toString();
    }

    private static String create_timestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }
}

微信的签名算法逻辑。 file

微信分享-常见错误及解决方法

调用config 接口的时候传入参数 debug: true 可以开启debug模式,页面会alert出错误信息。以下为常见错误及解决方法:

1.invalid url domain当前页面所在域名与使用的appid没有绑定,请确认正确填写绑定的域名,仅支持80(http)和443(https)两个端口,因此不需要填写端口号(一个appid可以绑定三个有效域名,见 ]目录1.1.1)。

2.invalid signature签名错误。建议按如下顺序检查:

1.确认签名算法正确,可用http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 页面工具进行校验。

2.确认config中nonceStr(js中驼峰标准大写S), timestamp与用以签名中的对应noncestr, timestamp一致。

3.确认url是页面完整的url(请在当前页面alert(location.href.split('#')[0])确认),包括'http(s)://'部分,以及'?'后面的GET参数部分,但不包括'#'hash后面的部分。

4.确认 config 中的 appid 与用来获取 jsapi_ticket 的 appid 一致。

5.确保一定缓存access_token和jsapi_ticket。

6.确保你获取用来签名的url是动态获取的,动态页面可参见实例代码中php的实现方式。如果是html的静态页面在前端通过ajax将url传到后台签名,前端需要用js获取当前页面除去'#'hash部分的链接(可用location.href.split('#')[0]获取,而且需要encodeURIComponent),因为页面一旦分享,微信客户端会在你的链接末尾加入其它参数,如果不是动态获取当前链接,将导致分享后的页面签名失败。

3.the permission value is offline verifying这个错误是因为config没有正确执行,或者是调用的JSAPI没有传入config的jsApiList参数中。建议按如下顺序检查:

1.确认config正确通过。

2.如果是在页面加载好时就调用了JSAPI,则必须写在wx.ready的回调中。

3.确认config的jsApiList参数包含了这个JSAPI。

4.permission denied该公众号没有权限使用这个JSAPI,或者是调用的JSAPI没有传入config的jsApiList参数中(部分接口需要认证之后才能使用)。

5.function not exist当前客户端版本不支持该接口,请升级到新版体验。

6.为什么6.0.1版本config:ok,但是6.0.2版本之后不ok(因为6.0.2版本之前没有做权限验证,所以config都是ok,但这并不意味着你config中的签名是OK的,请在6.0.2检验是否生成正确的签名以保证config在高版本中也ok。)

7.在iOS和Android都无法分享(请确认公众号已经认证,只有认证的公众号才具有分享相关接口权限,如果确实已经认证,则要检查监听接口是否在wx.ready回调函数中触发)

8.服务上线之后无法获取jsapi_ticket,自己测试时没问题。(因为access_token和jsapi_ticket必须要在自己的服务器缓存,否则上线后会触发频率限制。请确保一定对token和ticket做缓存以减少2次服务器请求,不仅可以避免触发频率限制,还加快你们自己的服务速度。目前为了方便测试提供了1w的获取量,超过阀值后,服务将不再可用,请确保在服务上线前一定全局缓存access_token和jsapi_ticket,两者有效期均为7200秒,否则一旦上线触发频率限制,服务将不再可用)。

9.uploadImage怎么传多图(目前只支持一次上传一张,多张图片需等前一张图片上传之后再调用该接口)

10.没法对本地选择的图片进行预览(chooseImage接口本身就支持预览,不需要额外支持)

11.通过a链接(例如先通过微信授权登录)跳转到b链接,invalid signature签名失败(后台生成签名的链接为使用jssdk的当前链接,也就是跳转后的b链接,请不要用微信登录的授权链接进行签名计算,后台签名的url一定是使用jssdk的当前页面的完整url除去'#'部分)

12.出现config:fail错误(这是由于传入的config参数不全导致,请确保传入正确的appId、timestamp、nonceStr、signature和需要使用的jsApiList)

13.如何把jsapi上传到微信的多媒体资源下载到自己的服务器(请参见文档中uploadVoice和uploadImage接口的备注说明)

14.Android通过jssdk上传到微信服务器,第三方再从微信下载到自己的服务器,会出现杂音(微信团队已经修复此问题,目前后台已优化上线)

15.绑定父级域名,是否其子域名也是可用的(是的,合法的子域名在绑定父域名之后是完全支持的)

16.在iOS微信6.1版本中,分享的图片外链不显示,只能显示公众号页面内链的图片或者微信服务器的图片,已在6.2中修复

17.是否需要对低版本自己做兼容(jssdk都是兼容低版本的,不需要第三方自己额外做更多工作,但有的接口是6.0.2新引入的,只有新版才可调用)

18.该公众号支付签名无效,无法发起该笔交易(请确保你使用的jweixin.js是官方线上版本,不仅可以减少用户流量,还有可能对某些bug进行修复,拷贝到第三方服务器中使用,官方将不对其出现的任何问题提供保障,具体支付签名算法可参考 JSSDK微信支付一栏)

19.目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题已在Android6.2中修复

20.uploadImage在chooseImage的回调中有时候Android会不执行,Android6.2会解决此问题,若需支持低版本可以把调用uploadImage放在setTimeout中延迟100ms解决

21.require subscribe错误说明你没有订阅该测试号,该错误仅测试号会出现

22.getLocation返回的坐标在openLocation有偏差,因为getLocation返回的是gps坐标,openLocation打开的腾讯地图为火星坐标,需要第三方自己做转换,6.2版本开始已经支持直接获取火星坐标

23.查看公众号(未添加): "menuItem:addContact"不显示,目前仅有从公众号传播出去的链接才能显示,来源必须是公众号

24.ICP备案数据同步有一天延迟,所以请在第二日绑定

今天我踩的坑是这样的,我用的redis然后保存到根据正式的token和ticket 然后我appid换成测试了 一直报签名错误,对比了很长时间,自己生成的和官网的一模一样就是报错。提示就是签名报错 他也不说哪里错了。

前几天有人问我怎么申请测试公众号,只能用于开发。

我把微信官方的地址给大家一下:https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index 直接用微信登录就可以 只能用于微信公众号开发。

下面我给你大家截图一下我的测试号。 filefilefile

微信公众号必不可少的工具。 下载地址:https://mp.weixin.qq.com/wiki?action=doc&id=mp1455784140&t=0.20035047813405238#5 file

由于有公司信息我部分打码,请大家理解。


这就是微信二次分享,哪里不懂可以私信我哦!


鼓励作者写出更好的技术文档,就请我喝一瓶哇哈哈哈哈哈哈哈。。

微信:

支付宝:


感谢一路支持我的人。。。。。

Love me and hold me
QQ:69673804(16年老号)
EMAIL:69673804@qq.com
友链交换
如果有兴趣和本博客交换友链的话,请按照下面的格式在评论区进行评论,我会尽快添加上你的链接。

网站名称:KingYiFan’S Blog
网站地址:http://blog.cnbuilder.cn
网站描述:年少是你未醒的梦话,风华是燃烬的彼岸花。
网站Logo/头像:[头像地址](https://blog.cnbuilder.cn/upload/2018/7/avatar20180720144536200.jpg)

推荐阅读