微信公众平台开发:JS-SDK之分享功能整理(Java)

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/dcb_ripple/article/details/52066708

   在偶然间,需要做一个公众账号分享的功能,细一想~  奈何宝宝不会呀,听都没听过肿么破,当时就心累了

  言归正传,由于我所有的开发代码均是在又各位前辈在度娘中留下的辛勤汗水,再由于自己本身就渣,属于能够通过查找资料的方式完成代码就谢天谢地那种类型,所以呢以下所有内容均属于从度娘中获取,只是把自己做过的,寻找过的资料做一个整理

大概

   在开发前,首先必须先要肯定是拥有一个微信公众平台账号,并且此微信公众号是需要通过验证并经微信授权认可的,无公众账号公司有赶着开发的,出门右转找上头要账号,不给直接揍! 

原理

其实我也不懂,就不乱说了,大致就是微信公众账号平台有提供一个js-sdk开发文档,有一定的开发规范,对又到分享等违规行为也有严格要求。按着开发文档做,区别就是中通过后台验证一下APPID与SECRET是否正确,是否授权等,恶心之处就在后台验证时。JS-SDK文档在 这里 .  
步骤(此处未尝试):

  首先下载官方的示例代码:http://demo.open.weixin.qq.com/jssdk/sample.zip

       此代码包括:jweixin-1.js、demo.js、style.css、jssdk.html。以上代码略加修改即可以用,下面是具体的实现方法四步曲:

       步骤一:绑定域名,先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”;

       步骤二:引入JS文件,在需要调用JS接口的页面引入JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.0.0.js

       步骤三:通过config接口注入权限验证配置,所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用);

        步骤四:通过ready接口处理成功验证。

        因此,这里最重要的一步是步骤三,获取config配置的签名signature ,如下:

wx.config({

    debug: true,  // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    appId: '',    // 必填,公众号的唯一标识
    timestamp: ,  // 必填,生成签名的时间戳
    nonceStr: '', // 必填,生成签名的随机串
    signature: '',// 必填,签名,见附录1
    jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});

实现

在开发之前肯定是需要几个从公众平台账号中拿到的开发者id,如不知道是什么,找领导要不给就干嘛~~  好了,由于此处也是领导给的APPID编号这些,所以具体我也不清楚怎么来的。。。。  百度上应该可以找到吧~ 
(一)实体类 TokenJson
**
 * @author Allen
 * @version 1.0
 * 创建时间:2016年4月12日 下午4:54:58
 */
public class TokenJson {
	private String access_token;
	private int expires_in;
	
	public String getAccess_token() {
		return access_token;
	}
	public void setAccess_token(String access_token) {
		this.access_token = access_token;
	}
	public int getExpires_in() {
		return expires_in;
	}
	public void setExpires_in(int expires_in) {
		this.expires_in = expires_in;
	}
	
	
}

实体类TicketJson
/**
 * @author Allen
 * @version 1.0
 * 创建时间:2016年4月12日 下午5:03:14
 */
public class TicketJson {
	private int errcode;
	private String errmsg;
	private String ticket;
	private String 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 String getExpires_in() {
		return expires_in;
	}
	public void setExpires_in(String expires_in) {
		this.expires_in = expires_in;
	}
}

HttpGetRequest用于连接验证服务器
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
/**
 * @author Allen
 * @version 1.0 创建时间:2016年4月12日 下午4:30:23
 */
public class HttpGetRequest {

	/**
	 * Get Request
	 * 
	 * @return
	 * @throws Exception
	 */
	public static String doGet(String url) throws Exception {
		URL localURL = new URL(url);
		URLConnection connection = localURL.openConnection();
		HttpURLConnection httpURLConnection = (HttpURLConnection) connection;
		httpURLConnection.setRequestProperty("Accept-Charset", "utf-8");
		httpURLConnection.setRequestProperty("Content-Type",
				"application/text");

		InputStream inputStream = null;
		InputStreamReader inputStreamReader = null;
		BufferedReader reader = null;
		StringBuffer resultBuffer = new StringBuffer();
		String tempLine = null;

		if (httpURLConnection.getResponseCode() >= 300) {
			throw new Exception(
					"HTTP Request is not success, Response code is "
							+ httpURLConnection.getResponseCode());
		}

		try {
			inputStream = httpURLConnection.getInputStream();
			inputStreamReader = new InputStreamReader(inputStream);
			reader = new BufferedReader(inputStreamReader);

			while ((tempLine = reader.readLine()) != null) {
				resultBuffer.append(tempLine);
			}

		} finally {

			if (reader != null) {
				reader.close();
			}

			if (inputStreamReader != null) {
				inputStreamReader.close();
			}

			if (inputStream != null) {
				inputStream.close();
			}

		}
		return resultBuffer.toString();
	}
}

如果有https SSL验证可用也可如下:
    /** 
     * 发起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) {  
            Logger.getAnonymousLogger().info("Weixin server connection timed out.");  
        } catch (Exception e) {  
            Logger.getAnonymousLogger().info("信任管理器请求时..."+e);  
        }  
        return jsonObject;  
    }  
MyX509TrustManager 这个不用纠结是什么,它是用于验证SSL的一个自定义Class,此方法不可直接用,需要做一些相应更改,如HTTPS验证方式,SSL证书等等,自我调整应该即可。。。。 (自白:没有通过此方法实现HTTPS,而是使用了HttpGetRequest

WxParams 实体类 用于记录从weixin官网得到的token与ticket
/**
 * @author Allen
 * @version 1.0
 * 创建时间:2016年4月13日 下午3:53:57
 */
public class WxParams {
	public static String token;
	public static String tokenTime;
	public static String tokenExpires;
	
	public static String ticket;
	public static String ticketTime;
	public static String ticketExpires;
}

Sign 用于整理整合得到的值 (虽然我不懂,哈哈哈~~)
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class Sign {
	
    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);
    }
}

WxUtil 工具类 js中的入口
import java.util.Map;

import net.sf.json.JSONObject;
import wrt.book.Json.TicketJson;
import wrt.book.Json.TokenJson;

/**
 * @author Allen
 * @version 1.0
 * 创建时间:2016年4月12日 下午4:28:41
 */
public class WxUtil {
	
	//此处的appid与wx.config 参数appId一致   微信公众账号提供给开发者的信息,以下同理
	public static String APPID = "wxb5571f0bxxxxxxxxx";
	
	//同上
	public static String SECRET = "c73435d332dxxxxxxxxxxx";
	
	private static TokenJson getAccess_token(){

		String url = String.format("https://api.weixin.qq.com/xxxxxxxxxxxxxxxxxxxx",APPID,SECRET);
		try {
			String result = HttpGetRequest.doGet(url);
			System.out.println("微信服务器获取token:"+result);
			JSONObject rqJsonObject = JSONObject.fromObject(result);
			TokenJson tokenJson = (TokenJson) JSONObject.toBean(rqJsonObject,TokenJson.class);
			return tokenJson;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
	}
	
	
	private static TicketJson getTicket(String token){
		String url = String.format("http://api.weixin.qq.com/xxxxxxxxxxxxxxxxxxxxxxxx",token);
		try {
			String result = HttpGetRequest.doGet(url);
			System.out.println("微信服务器获取Ticket:"+result);
			JSONObject rqJsonObject = JSONObject.fromObject(result);
			TicketJson ticket = (TicketJson) JSONObject.toBean(rqJsonObject,TicketJson.class);
			return ticket;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * 获取js sdk 认证信息
	* @author 
	* @date 创建时间 2016年7月28日 上午11:25:01 
	* @param url
	* @return
	 */
	public static Map<String, String> getSign(String url){
		
		//处理token失效的问题
		try {
			long tokenTimeLong = Long.parseLong(WxParams.tokenTime);
			long tokenExpiresLong = Long.parseLong(WxParams.tokenExpires);
			
			//时间差
			long differ = (System.currentTimeMillis() - tokenTimeLong) /1000;
			if (WxParams.token == null ||  differ > (tokenExpiresLong - 1800)) {
				System.out.println("token为null,或者超时,重新获取");
				TokenJson tokenJson = getAccess_token();
				if (tokenJson != null) {
					WxParams.token = tokenJson.getAccess_token();
					WxParams.tokenTime = System.currentTimeMillis()+"";
					WxParams.tokenExpires = tokenJson.getExpires_in()+"";
				}
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			TokenJson tokenJson = getAccess_token();
			if (tokenJson != null) {
				WxParams.token = tokenJson.getAccess_token();
				WxParams.tokenTime = System.currentTimeMillis()+"";
				WxParams.tokenExpires = tokenJson.getExpires_in()+"";
			}
		}

		//处理ticket失效的问题
		try {
			long ticketTimeLong = Long.parseLong(WxParams.ticketTime);
			long ticketExpiresLong = Long.parseLong(WxParams.ticketExpires);
			
			//时间差
			long differ = (System.currentTimeMillis() - ticketTimeLong) /1000;
			if (WxParams.ticket == null ||  differ > (ticketExpiresLong - 1800)) {
				System.out.println("ticket为null,或者超时,重新获取");
				TicketJson ticketJson = getTicket(WxParams.token);
				if (ticketJson != null) {
					WxParams.ticket = ticketJson.getTicket();
					WxParams.ticketTime = System.currentTimeMillis()+"";
					WxParams.ticketExpires = ticketJson.getExpires_in()+"";
				}
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			TicketJson ticketJson = getTicket(WxParams.token);
			if (ticketJson != null) {
				WxParams.ticket = ticketJson.getTicket();
				WxParams.ticketTime = System.currentTimeMillis()+"";
				WxParams.ticketExpires = ticketJson.getExpires_in()+"";
			}
		}

		Map<String, String> ret = Sign.sign(WxParams.ticket, url);
		System.out.println("计算出的签名-----------------------");
        for (Map.Entry entry : ret.entrySet()) {
            System.out.println(entry.getKey() + ", " + entry.getValue());
        }
        System.out.println("-----------------------");
        return ret;
	}

}

整个后台代码,全献上了,复制粘贴更改关键部分即可使用,不管你们能不能用,反正我是可以用~ 2333333333。
index.jsp  通过<%%> 嵌入
<%
//签名
String url = request.getScheme()+"://";
url+=request.getHeader("host");   
url+=request.getRequestURI();   
if(request.getQueryString()!=null){
	url+="?"+request.getQueryString();   
}

Map<String,String> sign = WxUtil.getSign(url);
String timestamp = sign.get("timestamp");
String nonceStr = sign.get("nonceStr");
String jsapi_ticket = sign.get("jsapi_ticket");
String signature = sign.get("signature");
//String url = sign.get("url");

%>

拿到后台反馈的信息后,使用wx.config({}) ;进行初始化操作如下:
<script type="text/javascript" src="jweixin-1.0.0.js"></script>
<script type="text/javascript">

wx.config({
    debug: false,
    appId: <%="'"+WxUtil.APPID+"'"%>,
    timestamp: <%="'"+timestamp+"'"%>,
    nonceStr: <%="'"+nonceStr+"'"%>,
    signature: <%="'"+signature+"'"%>,
    jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareQZone'] // 功能列表,我们要使用JS-SDK的什么功能
});


// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在 页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready 函数中。
wx.ready(function(){
    // 获取"分享到朋友圈"按钮点击状态及自定义分享内容接口
    wx.onMenuShareTimeline({
        title: '慧锐通电子书架', // 分享标题
        link: 'http://www.wrtrd.net/book/',
        imgUrl: 'http://www.wrtrd.net/book/images/wxbook.jpg' // 分享图标
    });
	
	
    // 获取"分享给朋友"按钮点击状态及自定义分享内容接口
    wx.onMenuShareAppMessage({
        title: '慧锐通电子书架', // 分享标题
        desc: '慧锐通产品介绍的电子画册,含数字对讲、模拟对讲、云对讲、智能互联、蓝牙门禁等系统!', // 分享描述
        link: 'http://www.wrtrd.net/book/',
        imgUrl: 'http://www.wrtrd.net/book/images/wxbook.jpg', // 分享图标
        type: 'link' // 分享类型,music、video或link,不填默认为link
    });
	
	
	//获取"分享到QQ"按钮点击状态及自定义分享内容接口
	wx.onMenuShareQQ({
    title: '慧锐通电子书架', // 分享标题
    desc: '慧锐通产品介绍的电子画册,含数字对讲、模拟对讲、云对讲、智能互联、蓝牙门禁等系统!', // 分享描述
    link: 'http://www.wrtrd.net/book/', // 分享链接
    imgUrl: 'http://www.wrtrd.net/book/images/wxbook.jpg' // 分享图标
	});
	
	
	//获取"分享到腾讯微博"按钮点击状态及自定义分享内容接口
	wx.onMenuShareWeibo({
    title: '慧锐通电子书架', // 分享标题
    desc: '慧锐通产品介绍的电子画册,含数字对讲、模拟对讲、云对讲、智能互联、蓝牙门禁等系统!', // 分享描述
    link: 'http://www.wrtrd.net/book/', // 分享链接
    imgUrl: 'http://www.wrtrd.net/book/images/wxbook.jpg' // 分享图标
	});
	
	
	//获取"分享到QQ空间"按钮点击状态及自定义分享内容接口
	wx.onMenuShareQZone({
    title: '慧锐通电子书架', // 分享标题
    desc: '慧锐通产品介绍的电子画册,含数字对讲、模拟对讲、云对讲、智能互联、蓝牙门禁等系统!', // 分享描述
    link: 'http://www.wrtrd.net/book/', // 分享链接
    imgUrl: 'http://www.wrtrd.net/book/images/wxbook.jpg' // 分享图标
	});
	
});

</script>

以上是全部代码,基本稍加更改则可直接使用。

其中,wx.config({})是整体的关键,在页面初始化时会自动的验证后台信息,验证成功会自动调用wx.ready(function(){ }
wx.ready(function(){

    // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});
验证失败调用
wx.error(function(res){

    // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。

});

使用注意事项看这里:
看这里看这里 。 补充两点,如果以上代码都编辑后,测试时发现 wx.config验证通过,基本上按照JS-SDK开发文档来就足够了。同理,如果wx.config验证不通过,请直接百度可能出现的原因,有可能是提供的开发者账号等错误,并有一个隐藏的错误,传入后台的URL与当前页面URL不匹配,此时也不会通过 ~~~ T~T 被这个原因坑了一整天,
同时,测试时,必须部署到微信公众平台账号中,因为它会检测当前传入的HTTP地址是否与备案的相同,如若不同则会一直报异常,切记切记。。。
无奈本屌只会用,不懂其原理,惭愧惭愧,不过能解决的就是好的不是嘛~ 最后附上相关资料信息
微信开发:JS-SDK之分享接口的实现
 微信开发(六)微信分享接入
wx.onMenuShareTimeline使用注意事项
感谢那些为此默默付出的大神们~~  咱们谢谢他们哒,同时新手勿喷勿喷诺~  
展开阅读全文

没有更多推荐了,返回首页