2016-07-29 18:31:38 dcb_ripple 阅读数 18251
  • 微信公众平台深度开发Java版v2.0单品课程——微信WEB...

    “微信公众平台深度开发Java版 v2.0”系列课程共有6季,使用JAVA语言,系统讲解微信公众平台订阅号、服务号官方列出的全部功能接口,包括:自定义菜单、个性化菜单(按需定制菜单)、群发消息、客服消息(有限次消息推送)、模板消息接(无限次消息推送)、微信网页开发(微信WEB开发、微信游戏)、微信JSSDK开发、用户管理、获取用户基本信息、网页授权获取用户基本信息(通过WEB得到用户信息)、二维码(临时二维码、永久二维码)、事件推送、接收普通消息、被动回复用户消息,等知识点。 以及,针对微信公众号开发的服务端架构设计方案。课程以微信公众号开发视角,讲解JAVA开发微信公众号所需的框架、第三方工具。 购买套餐还赠送经典微信开发课程——[微信公众号_独立知识点]环境搭建。该课程针对各种复杂的网络环境,讲解如何构建开发环境,已解决“没有服务器”、“没有固定IP”等开发者遇到的窘境。 课程采用独立知识点讲解,一个知识点,一组课程,真正做到“简单、高效、”以短的时间、实现的学习。更多课程信息请访问CSDN。网址:http://edu.csdn.net/lecturer/631 “微信公众平台企业号开发Java版”陆续上线。 详情 qq2326321088

    7574 人正在学习 去看看 翟东平

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

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

大概

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

原理

其实我也不懂,就不乱说了,大致就是微信公众账号平台有提供一个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使用注意事项
感谢那些为此默默付出的大神们~~  咱们谢谢他们哒,同时新手勿喷勿喷诺~  
2018-04-24 20:02:24 qq_39879632 阅读数 533
  • 微信公众平台深度开发Java版v2.0单品课程——微信WEB...

    “微信公众平台深度开发Java版 v2.0”系列课程共有6季,使用JAVA语言,系统讲解微信公众平台订阅号、服务号官方列出的全部功能接口,包括:自定义菜单、个性化菜单(按需定制菜单)、群发消息、客服消息(有限次消息推送)、模板消息接(无限次消息推送)、微信网页开发(微信WEB开发、微信游戏)、微信JSSDK开发、用户管理、获取用户基本信息、网页授权获取用户基本信息(通过WEB得到用户信息)、二维码(临时二维码、永久二维码)、事件推送、接收普通消息、被动回复用户消息,等知识点。 以及,针对微信公众号开发的服务端架构设计方案。课程以微信公众号开发视角,讲解JAVA开发微信公众号所需的框架、第三方工具。 购买套餐还赠送经典微信开发课程——[微信公众号_独立知识点]环境搭建。该课程针对各种复杂的网络环境,讲解如何构建开发环境,已解决“没有服务器”、“没有固定IP”等开发者遇到的窘境。 课程采用独立知识点讲解,一个知识点,一组课程,真正做到“简单、高效、”以短的时间、实现的学习。更多课程信息请访问CSDN。网址:http://edu.csdn.net/lecturer/631 “微信公众平台企业号开发Java版”陆续上线。 详情 qq2326321088

    7574 人正在学习 去看看 翟东平

Java微信分享,步骤是

1、根据当前的url,获取signature,nonceStr,timestamp 和appId。 
2、通过signature,nonceStr,timestamp 和appId来配置微信 wx.config。 
3、通过wx.ready实现微信分享功能。

1、html端

引入微信JS-SDK.

<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>

//分享核心js代码
$(document).ready(function () {
    //通过ajax,在页面加载的时候获取微信分享接口signature,nonceStr,timestamp 和appId
    $.ajax({
        type: "post",
        url: "/weixin/share",
        dataType: "json",
        data:"url="+window.location.href,
        success: function (data) {
            wx.config({
                debug: false,
                appId: data.appId,
                timestamp: data.timestamp,
                nonceStr: data.nonceStr,
                signature: data.signature,
                jsApiList: ['onMenuShareAppMessage', 'onMenuShareTimeline', 'hideAllNonBaseMenuItem', 'showMenuItems']
                // 功能列表,我们要使用JS-SDK的什么功能
            });
            wx.ready(function () {
                // 获取“分享给朋友”按钮点击状态及自定义分享内容接口
                wx.onMenuShareAppMessage({
                    title: "分享自定义标题", // 分享标题
                    desc: "分享自定义描述", // 分享描述
                    link: "http://localhost/weixin/share?openId=1",//分享点击之后的链接
                    imgUrl:'/images/photo/1.jpg', // 分享图标
                    type: 'link', // 分享类型,music、video或link,不填默认为link
                    success: function () {
                        //成功之后的回调
                    }
                });
                wx.hideAllNonBaseMenuItem();
                wx.showMenuItems({
                    menuList: ['menuItem:share:appMessage', 'menuItem:share:timeline'] // 要隐藏的菜单项,只能隐藏“传播类”和“保护类”按钮,所有menu项见附录3
                });
                wx.onMenuShareTimeline({
                    title: "分享自定义标题", // 分享标题
                    desc: "分享自定义描述", // 分享描述
                    link: "http://localhost/weixin/share?openId=1",//分享点击之后的链接
                    imgUrl:'/images/photo/1.jpg', // 分享图标
                    type: 'link', // 分享类型,music、video或link,不填默认为link
                    success: function () {
                        //成功之后的回调
                    }
                    cancel: function () {
                        // 用户取消分享后执行的回调函数
                    }
                });
            });
            wx.error(function (res) {
                //打印错误消息。及把 debug:false,设置为debug:ture就可以直接在网页上看到弹出的错误提示
            });
        }
    })
});

2、Java代码,获取 signature,nonceStr,timestamp 和appId

    @RequestMapping(value = "/share", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> share(HttpServletRequest request) {
        String urlTemp = "http://" + request.getServerName() + request.getContextPath();
        String urlpath = "http://" + request.getServerName();
        String appUrl = request.getParameter("url");
        if (request.getParameter("code") != null) {
            appUrl += "&code=" + request.getParameter("code");
        }
        if (request.getParameter("state") != null) {
            appUrl += "&state=" + request.getParameter("state");
        }
        return WxConfigUtil.getSignature(appUrl, ContentValues.APPID, ContentValues.SECRET, urlTemp, urlpath);
    }

工具类我就把整个贴上来了,其中有些方法是没有用到的。

getSignature()整个方法是微信分享中的核心方法,用来获取signature,nonceStr,timestamp 和appId这几个核心参数。

package com.blog.common.util;

import com.alibaba.fastjson.JSONObject;
import com.blog.common.model.Token;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
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.Arrays;
import java.util.HashMap;
import java.util.Map;


/**
 * 公众平台通用接口工具类
 *
 * @author james
 * @date 2015-02-27
 */
public class WxConfigUtil {
    // 获取access_token的接口地址(GET) 限2000(次/天)
    public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    // 获取jsapi_ticket的接口地址(GET) 限2000(次/天)
    public final static String jsapi_ticket_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
    // 缓存添加的时间
    public static String cacheAddTime = null;
    // token,ticket缓存
    public static Map<String, Token> TOKEN_TICKET_CACHE = new HashMap<String, Token>();
    // token对应的key
    private static final String TOKEN = "token";
    // ticket对应的key
    private static final String TICKET = "ticket";

    /**
     * 外部获取签名入口类
     *
     * @param appUrl    应用的url
     * @return
     */
    public static Map<String, Object> getSignature(String appUrl, String appId, String secret, String url, String urlpath) {
        // 生成签名的随机串
        String noncestr = RandomUtil.getStringRandom(4);
        if (appUrl == null || "".equals(appUrl)) {
            return null;
        }
        String signature = null;
        Token accessTocken = getToken(appId, secret, System.currentTimeMillis() / 1000);
        Token accessTicket = getTicket(accessTocken.getToken(), System.currentTimeMillis() / 1000);
        signature = signature(accessTicket.getTicket(), cacheAddTime, noncestr, appUrl);
        System.out.println("-=-=-=-=-=-=-=-=appUrl:" + appUrl);
        System.out.println("-=-=-=-=-=-=-=-=token:" + accessTocken.getToken());
        System.out.println("-=-=-=-=-=-=-=-=ticket:" + accessTicket.getTicket());
        System.out.println("-=-=-=-=-=-=-=-=signature:" + signature);
        System.out.println("-=-=-=-=-=-=-=-=timestamp:" + cacheAddTime);
        Map<String, Object> map = new HashMap<>();
        map.put("appId", appId);
        map.put("timestamp", cacheAddTime);
        map.put("nonceStr", noncestr);
        map.put("appUrl", appUrl);
        map.put("signature", signature);
        map.put("url", url);
        map.put("urlpath", urlpath);
        return map;
    }

    /**
     * 获得Token
     *
     * @return
     */
    public static String getToken(String appId, String secret) {
        Token accessTocken = getToken(appId, secret, System.currentTimeMillis() / 1000);
        return accessTocken.getToken();
    }

    /**
     * 签名
     *
     * @param timestamp
     * @return
     */
    private static String signature(String jsapi_ticket, String timestamp, String noncestr, String url) {
        jsapi_ticket = "jsapi_ticket=" + jsapi_ticket;
        timestamp = "timestamp=" + timestamp;
        noncestr = "noncestr=" + noncestr;
        url = "url=" + url;
        String[] arr = new String[]{jsapi_ticket, noncestr, timestamp, url};
        // 将token、timestamp、nonce,url参数进行字典序排序
        Arrays.sort(arr);
        StringBuilder content = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            content.append(arr[i]);
            if (i != arr.length - 1) {
                content.append("&");
            }
        }
        MessageDigest md = null;
        String tmpStr = null;

        try {
            md = MessageDigest.getInstance("SHA-1");
            // 将三个参数字符串拼接成一个字符串进行sha1加密
            byte[] digest = md.digest(content.toString().getBytes());
            tmpStr = byteToStr(digest);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        content = null;
        return tmpStr;
    }

    /**
     * 获取access_token
     *
     * @param appid     凭证
     * @param appsecret 密钥
     * @return
     */
    public static Token getToken(String appid, String appsecret, long currentTime) {
        Token tockenTicketCache = getTokenTicket(TOKEN);
        Token Token = null;

        if (tockenTicketCache != null && (currentTime - tockenTicketCache.getAddTime() <= tockenTicketCache.getExpiresIn())) {// 缓存存在并且没过期
            System.out.println("==========缓存中token已获取时长为:" + (currentTime - tockenTicketCache.getAddTime()) + "毫秒,可以重新使用");
            return tockenTicketCache;
        }
        System.out.println("==========缓存中token不存在或已过期===============");
        String requestUrl = access_token_url.replace("APPID", appid).replace("APPSECRET", appsecret);
        JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
        // 如果请求成功
        if (null != jsonObject) {
            Token = new Token();
            Token.setToken(jsonObject.getString("access_token"));
            Token.setExpiresIn(jsonObject.getIntValue("expires_in") / 2);// 正常过期时间是7200秒,此处设置3600秒读取一次
            System.out.println("==========tocket缓存过期时间为:" + Token.getExpiresIn() + "毫秒");
            Token.setAddTime(currentTime);
            updateToken(TOKEN, Token);
        }
        return Token;
    }

    /**
     * 获取ticket
     *
     * @param token
     * @return
     */
    private static Token getTicket(String token, long currentTime) {
        Token tockenTicketCache = getTokenTicket(TICKET);
        Token Token = null;
        if (tockenTicketCache != null && (currentTime - tockenTicketCache.getAddTime() <= tockenTicketCache.getExpiresIn())) {// 缓存中有ticket
            System.out.println("==========缓存中ticket已获取时长为:" + (currentTime - tockenTicketCache.getAddTime()) + "毫秒,可以重新使用");
            return tockenTicketCache;
        }
        System.out.println("==========缓存中ticket不存在或已过期===============");
        String requestUrl = jsapi_ticket_url.replace("ACCESS_TOKEN", token);
        JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
        // 如果请求成功
        if (null != jsonObject) {
            Token = new Token();
            Token.setTicket(jsonObject.getString("ticket"));
            Token.setExpiresIn(jsonObject.getIntValue("expires_in") / 2);// 正常过期时间是7200秒,此处设置3600秒读取一次
            System.out.println("==========ticket缓存过期时间为:" + Token.getExpiresIn() + "毫秒");
            Token.setAddTime(currentTime);
            updateToken(TICKET, Token);
        }
        return Token;
    }

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

    /**
     * 将字节数组转换为十六进制字符串
     *
     * @param byteArray
     * @return
     */
    private static String byteToStr(byte[] byteArray) {
        String strDigest = "";
        for (int i = 0; i < byteArray.length; i++) {
            strDigest += byteToHexStr(byteArray[i]);
        }
        return strDigest;
    }

    /**
     * 将字节转换为十六进制字符串
     *
     * @param mByte
     * @return
     */
    private static String byteToHexStr(byte mByte) {

        char[] Digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        char[] tempArr = new char[2];
        tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
        tempArr[1] = Digit[mByte & 0X0F];

        String s = new String(tempArr);
        return s;
    }

    /**
     * 从缓存中读取token或者ticket
     *
     * @return
     */
    private static Token getTokenTicket(String key) {
        if (TOKEN_TICKET_CACHE != null && TOKEN_TICKET_CACHE.get(key) != null) {
            System.out.println("==========从缓存中获取到了" + key + "成功===============");
            return TOKEN_TICKET_CACHE.get(key);
        }
        return null;
    }

    /**
     * 更新缓存中token或者ticket
     *
     * @return
     */
    private static void updateToken(String key, Token accessTocken) {
        if (TOKEN_TICKET_CACHE != null && TOKEN_TICKET_CACHE.get(key) != null) {
            TOKEN_TICKET_CACHE.remove(key);
            System.out.println("==========从缓存中删除" + key + "成功===============");
        }
        TOKEN_TICKET_CACHE.put(key, accessTocken);
        cacheAddTime = String.valueOf(accessTocken.getAddTime());// 更新缓存修改的时间
        System.out.println("==========更新缓存中" + key + "成功===============");
    }

}

友情链接:https://blog.csdn.net/shrmuscles/article/details/77987185https://blog.csdn.net/qq_29057491/article/details/61191566

2017-01-14 10:39:38 crystalu3 阅读数 9218
  • 微信公众平台深度开发Java版v2.0单品课程——微信WEB...

    “微信公众平台深度开发Java版 v2.0”系列课程共有6季,使用JAVA语言,系统讲解微信公众平台订阅号、服务号官方列出的全部功能接口,包括:自定义菜单、个性化菜单(按需定制菜单)、群发消息、客服消息(有限次消息推送)、模板消息接(无限次消息推送)、微信网页开发(微信WEB开发、微信游戏)、微信JSSDK开发、用户管理、获取用户基本信息、网页授权获取用户基本信息(通过WEB得到用户信息)、二维码(临时二维码、永久二维码)、事件推送、接收普通消息、被动回复用户消息,等知识点。 以及,针对微信公众号开发的服务端架构设计方案。课程以微信公众号开发视角,讲解JAVA开发微信公众号所需的框架、第三方工具。 购买套餐还赠送经典微信开发课程——[微信公众号_独立知识点]环境搭建。该课程针对各种复杂的网络环境,讲解如何构建开发环境,已解决“没有服务器”、“没有固定IP”等开发者遇到的窘境。 课程采用独立知识点讲解,一个知识点,一组课程,真正做到“简单、高效、”以短的时间、实现的学习。更多课程信息请访问CSDN。网址:http://edu.csdn.net/lecturer/631 “微信公众平台企业号开发Java版”陆续上线。 详情 qq2326321088

    7574 人正在学习 去看看 翟东平

            问题与需求:h5页面的内容在微信分享后分享的内容无法定制,要自定义分享内容为该h5页面的标题,内容,图片等。

            实现与排错:由于本人技术有限,基本都是参照网络上大神们提供的源码,在部分地方做了简单修改。感谢大神们的无私奉献!主要参考链接和实现代码如下:

                                    java开发微信分享到朋友圈功能  http://www.jb51.net/article/88690.htm
                                    微信公众平台开发:JS-SDK之分享功能整理(Java)http://blog.csdn.net/dcb_ripple/article/details/52066708

                                    实现】步骤一:参考微信公众平台的开发者文档https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html了解开发步骤

                                                步骤二:为了描述方便,我先把h5页面的js代码贴出,再描述其实现所依赖的java代码。

1.1h5页面的js代码

       h5页面:(获取该页面中的title-标题 discuss-内容 传入wx.config中实现分享内容的定制)                                  
      



       ps.由于我的页面用了angularjs,在js中如果通过document.getElementById拿值总是为空所以在js中再次使用了ajax向后台取值!!很影响性能的做法,在js中如何拿到angularjs渲染后的值呢?还希望有知道的朋友指点一下。
       weshare.js代码:
      
//获取签名
$.ajax({
    type: "GET",
    url: "wshare/getSignature",
    data:{url:url},
    success: function(data){
        console.log("success");
        var objData=JSON.parse(data);
        timestamp=objData.timestamp;
        noncestr=objData.noncestr;
        signature=objData.signature;
        console.log(objData);
        wxShare();
    },
    statusCode: {404: function(){
        alert('page not found');
    }}
});
function wxShare(){
    wx.config({
        debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
        appId: 'wxca57e6589ccebff0', // 和获取Ticke的必须一样------必填,公众号的唯一标识
        timestamp:timestamp, // 必填,生成签名的时间戳
        nonceStr: noncestr, // 必填,生成签名的随机串
        signature: signature,// 必填,签名,见附录1
        jsApiList: [
            'onMenuShareAppMessage','onMenuShareTimeline','onMenuShareTimeline','onMenuShareQQ','onMenuShareQZone'
        ] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
    });
}
wx.ready(function(){
    //config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,
    //config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关
    //接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。

    //----------“分享给朋友”
    wx.onMenuShareAppMessage({
        title:title, // 分享标题
        desc:discuss, // 分享描述
        link: url, // 分享链接
        imgUrl: shareImgUrl, // 分享图标
        success: function () {
            // 用户确认分享后执行的回调函数、
        },
        cancel: function () {
            // 用户取消分享后执行的回调函数
        }
    });
    //------------"分享到朋友圈"
    wx.onMenuShareTimeline({
        title: title, // 分享标题
        desc: discuss, // 分享描述
        link: url, // 分享链接
        imgUrl: shareImgUrl, // 分享图标
        type: '', // 分享类型,music、video或link,不填默认为link
        dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
        success: function () {
            // 用户确认分享后执行的回调函数、
        },
        cancel: function () {
            // 用户取消分享后执行的回调函数
        }
    });
    //-------------分享到QQ
    wx.onMenuShareQQ({
        title: title, // 分享标题
        desc: discuss, // 分享描述
        link: url, // 分享链接
        imgUrl: shareImgUrl, // 分享图标
        type: '', // 分享类型,music、video或link,不填默认为link
        dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
        success: function () {
            // 用户确认分享后执行的回调函数、
        },
        cancel: function () {
            // 用户取消分享后执行的回调函数
        }
    });
    //-------------分享到QQ空间
    wx.onMenuShareQZone({
        title: title, // 分享标题
        desc: discuss, // 分享描述
        link: url, // 分享链接
        imgUrl: shareImgUrl, // 分享图标
        type: '', // 分享类型,music、video或link,不填默认为link
        dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
        success: function () {
            // 用户确认分享后执行的回调函数、
        },
        cancel: function () {
            // 用户取消分享后执行的回调函数
        }
    });

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



1.2java后台实现代码
      1.2.1 weixin.controller
      
package com.weixin.controller;

import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.weixin.util.WeixinUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.google.gson.Gson;
import com.weixin.model.Ticket;
import com.weixin.util.GetRandomStr;
import com.weixin.util.SignatureBean;



/**
 * Created by Administrator on 2017/1/9.
 */
@Controller
@RequestMapping("/wshare")
public class WeixinshareController {

        private static Ticket aticket = null;

        @RequestMapping("/getSignature")
        public String getSignature( HttpServletRequest request,
                                    HttpServletResponse response) throws IOException, ParseException{
            //获取签名页面链接
            String url = request.getParameter("url");
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            //获取标签,并检查标签是否过期
            if(aticket==null){//第一次访问,标签不存在。
                aticket = executeTicket(response,"1",url,format);
                return null;
            }else{//标签存在,判断标签是否超时
                String oldAcquiretime = aticket.getAcquiretime();
                long difference=format.parse(format.format(new Date())).getTime()-format.parse(oldAcquiretime).getTime();
                if(difference>7100000){//标签超时,重新到微信服务器请求标签超时时间为7200秒(7200000毫秒)
                    aticket = executeTicket(response,"2",url,format);
                    return null;
                }else{//标签未超时
                    /**
                     * 注意事项
                     * 1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。
                     * 2.签名用的url必须是调用JS接口页面的完整URL。
                     * 3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。
                     *
                     ****根据第1点要求   signature 配置的时候很容易出错,需要把生成 Ticket的 noncestr和 timestamp传给客户端***
                     */
                    System.out.print("ticket:"+aticket.getTicket());
                    String signature = signature(aticket.getTicket(),aticket.getTimestamp(),aticket.getNoncestr(),url);
                    SignatureBean signatureBean = new SignatureBean();
                    signatureBean.setNoncestr(aticket.getNoncestr());
                    signatureBean.setSignature(signature);
                    signatureBean.setTimestamp(aticket.getTimestamp());
                    signatureBean.setUrl(url);
                    response.setContentType("text/html;charset=UTF-8");
                    response.getWriter().print(new Gson().toJson(signatureBean));
                    return null;
                }
            }


        }
        /**
         更新和获取ticket的方法,因为用的solr所以更新和新增是一样的ID无则添加,有则更新
         */
        public Ticket executeTicket(HttpServletResponse response,String flag,String url,SimpleDateFormat format) throws IOException{

            //获取签名随机字符串
            GetRandomStr randomStr = new GetRandomStr();
            String noncestr = randomStr.getRandomString(15);
            //获取签名时间戳
            String timestamp = Long.toString(System.currentTimeMillis());
            //请求accessToken
            String accessTokenUrl ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wxca57e6589ccebff0&secret=b7d7c5df24644563be32bd84b90c8bf6";
            String tokenJson = WeixinUtil.httpRequest(accessTokenUrl, "GET", null);
            Gson gson = new Gson();
            ShareAccess_Token token = gson.fromJson(tokenJson, ShareAccess_Token.class);
            String to= token.getAccess_token();
            //获取标签
            String urlTicket ="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+to+"&type=jsapi";
           String ticketJson = WeixinUtil.httpRequest(urlTicket, "GET", null);
            Ticket ticket = gson.fromJson(ticketJson, Ticket.class);
            String t = ticket.getTicket();
            //String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");
            //我的Ticket ID是写死的 20160114wiimediamrylsong1152
            String acquiretime = format.format(new Date());
            ticket.setTid("20160114wiimediamrylsong1152");
            ticket.setAcquiretime(acquiretime);
            ticket.setTimestamp(timestamp);
            ticket.setNoncestr(noncestr);

            /**
             * 注意事项
             * 1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。
             * 2.签名用的url必须是调用JS接口页面的完整URL。
             * 3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。
             *
             *根据第1点要求   signature 配置的时候很容易出错,需要把生成 Ticket的 noncestr和 timestamp传给客户端*
             */

            String signature = signature(t,timestamp,noncestr,url);
            System.out.print("ticket: "+t);
            SignatureBean signatureBean = new SignatureBean();
            signatureBean.setNoncestr(noncestr);
            signatureBean.setSignature(signature);
            signatureBean.setTimestamp(timestamp);
            signatureBean.setUrl(url);
            response.setContentType("text/html;charset=UTF-8");
            response.getWriter().print(new Gson().toJson(signatureBean));

            return ticket;
        }

        /**
         *
         *

Project:mryl_phone_v2

* *

:mryl_phone_v2

* *

Description:根据标签,时间戳,密匙,URL进行签名

* *

Company:Wiimedia

* *@Athor:SongJia * *@Date:2016-7-15 上午09:37:13 * */ private String signature(String jsapi_ticket, String timestamp, String noncestr, String url) { jsapi_ticket = "jsapi_ticket=" + jsapi_ticket; timestamp = "timestamp=" + timestamp; noncestr = "noncestr=" + noncestr; url = "url=" + url; String[] arr = new String[] { jsapi_ticket, timestamp, noncestr, url }; // 将token、timestamp、nonce,url参数进行字典序排序 Arrays.sort(arr); StringBuilder content = new StringBuilder(); for (int i = 0; i < arr.length; i++) { content.append(arr[i]); if (i != arr.length - 1) { content.append("&"); } } MessageDigest md = null; String tmpStr = null; try { md = MessageDigest.getInstance("SHA-1"); // 将三个参数字符串拼接成一个字符串进行sha1加密 byte[] digest = md.digest(content.toString().getBytes()); tmpStr = byteToStr(digest); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } content = null; return tmpStr; } /** * 将字节转换为十六进制字符串 * * @param mByte * @return */ private static String byteToHexStr(byte mByte) { char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; char[] tempArr = new char[2]; tempArr[0] = Digit[(mByte >>> 4) & 0X0F]; tempArr[1] = Digit[mByte & 0X0F]; String s = new String(tempArr); return s; } /** * 将字节数组转换为十六进制字符串 * * @param byteArray * @return */ private static String byteToStr(byte[] byteArray) { String strDigest = ""; for (int i = 0; i < byteArray.length; i++) { strDigest += byteToHexStr(byteArray[i]); } return strDigest; } class ShareAccess_Token{ private String access_token; private String expires_in; public String getAccess_token() { return access_token; } public void setAccess_token(String accessToken) { access_token = accessToken; } public String getExpires_in() { return expires_in; } public void setExpires_in(String expiresIn) { expires_in = expiresIn; } } }
       原大神的实现中是将Ticket存入数据库中,但是考虑到TicketId已写死,Ticket只有一条数据,存入数据库,再从数据库中取值新等过于麻烦,故将Ticket作为static变量。第一次访问时创建,超时时给他赋新值。
      1.2.2weixin.util
      {CSDN:CODE:2126562
     
package com.weixin.util;

/**
 * Created by Administrator on 2017/1/9.
 */
public class SignatureBean {
    private String noncestr;
    private String url;
    private String timestamp;
    private String signature;
    public String getNoncestr() {
        return noncestr;
    }
    public void setNoncestr(String noncestr) {
        this.noncestr = noncestr;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getTimestamp() {
        return timestamp;
    }
    public void setTimestamp(String timestamp) {
        this.timestamp = timestamp;
    }
    public String getSignature() {
        return signature;
    }
    public void setSignature(String signature) {
        this.signature = signature;
    }

}

     
package com.weixin.util;

import java.util.Random;

/**
 * Created by Administrator on 2017/1/9.
 * 获取随机字符串
 */

public class GetRandomStr {
    public String getRandomString(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }
}
  
     
package com.weixin.util;

import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * Created by Administrator on 2017/1/10.
 */
public class MyX509TrustManager implements X509TrustManager {
    @Override
    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

    }

    @Override
    public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }
}

      ps.因为大神的注释写的很清楚,拉入我的项目中是可用滴,当然关于这个MyX509TrustManager我也不懂,百度之后还是不知道应该信任哪些证书,然后,干脆什么都没改,信任全部。当然网上也有很多不是通过Https请求的,然而根据微信现在的规定,好像都需要Https请求了。对于这个MyX509TrustManager大家有什么好的实现欢迎指教。
      1.2.3weixin.model     
     
package com.weixin.model;

/**
 * Created by Administrator on 2017/1/9.
 */
public class Ticket {
    private String tid;
    private String ticket;
    private String errcode;
    private String errmsg;
    private String expires_in;
    private String acquiretime;
    private String noncestr;
    private String timestamp;
    public Ticket(String tid, String ticket, String errcode, String errmsg,
                  String expiresIn, String acquiretime, String noncestr,
                  String timestamp) {
        super();
        this.tid = tid;
        this.ticket = ticket;
        this.errcode = errcode;
        this.errmsg = errmsg;
        expires_in = expiresIn;
        this.acquiretime = acquiretime;
        this.noncestr = noncestr;
        this.timestamp = timestamp;
    }
    public String getTid() {
        return tid;
    }
    public void setTid(String tid) {
        this.tid = tid;
    }
    public String getTicket() {
        return ticket;
    }
    public void setTicket(String ticket) {
        this.ticket = ticket;
    }
    public String getErrcode() {
        return errcode;
    }
    public void setErrcode(String errcode) {
        this.errcode = errcode;
    }
    public String getErrmsg() {
        return errmsg;
    }
    public void setErrmsg(String errmsg) {
        this.errmsg = errmsg;
    }
    public String getExpires_in() {
        return expires_in;
    }
    public void setExpires_in(String expiresIn) {
        expires_in = expiresIn;
    }
    public String getAcquiretime() {
        return acquiretime;
    }
    public void setAcquiretime(String acquiretime) {
        this.acquiretime = acquiretime;
    }
    public String getNoncestr() {
        return noncestr;
    }
    public void setNoncestr(String noncestr) {
        this.noncestr = noncestr;
    }
    public String getTimestamp() {
        return timestamp;
    }
    public void setTimestamp(String timestamp) {
        this.timestamp = timestamp;
    }
}

      最后,由于是在项目下新开的包,别忘了在Application.java中加入
@ComponentScan(basePackages = {"com.mzqadmin","com.baidu","com.weixin"})
      另外,调试时把debug:true如果debug:ok却始终无法完成定制,一定是你的js代码有问题,后台验证已经通过了,看看你的js代码里参数是否带“”什么的。别问我怎么知道的,低级错误害死人呐~
      以上,是我做微信分享功能参考和实现的所有代码,有很多不足之处,还希望各位看官不吝赐教。
     
   
            




      
      
                        

                                                

                                                
  


                                        

                                                        

                                   

                                 

2016-10-30 12:04:22 doc_wei 阅读数 6134
  • 微信公众平台深度开发Java版v2.0单品课程——微信WEB...

    “微信公众平台深度开发Java版 v2.0”系列课程共有6季,使用JAVA语言,系统讲解微信公众平台订阅号、服务号官方列出的全部功能接口,包括:自定义菜单、个性化菜单(按需定制菜单)、群发消息、客服消息(有限次消息推送)、模板消息接(无限次消息推送)、微信网页开发(微信WEB开发、微信游戏)、微信JSSDK开发、用户管理、获取用户基本信息、网页授权获取用户基本信息(通过WEB得到用户信息)、二维码(临时二维码、永久二维码)、事件推送、接收普通消息、被动回复用户消息,等知识点。 以及,针对微信公众号开发的服务端架构设计方案。课程以微信公众号开发视角,讲解JAVA开发微信公众号所需的框架、第三方工具。 购买套餐还赠送经典微信开发课程——[微信公众号_独立知识点]环境搭建。该课程针对各种复杂的网络环境,讲解如何构建开发环境,已解决“没有服务器”、“没有固定IP”等开发者遇到的窘境。 课程采用独立知识点讲解,一个知识点,一组课程,真正做到“简单、高效、”以短的时间、实现的学习。更多课程信息请访问CSDN。网址:http://edu.csdn.net/lecturer/631 “微信公众平台企业号开发Java版”陆续上线。 详情 qq2326321088

    7574 人正在学习 去看看 翟东平
这篇文章主要为大家详细介绍了java开发微信发送给朋友和分享到朋友圈功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

微信分享功能开发

用了一天时间,把微信发送给朋友和分享到朋友圈功能开发出来,在这里给大家分享一下,避免大家走弯路。

一、服务器端程序

package com.wiimedia.controller;
 
 
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
 
import com.google.gson.Gson;
import com.wiimedia.model.Ticket;
import com.wiimedia.service.ArticleSolrService;
import com.wiimedia.service.TicketRepository;
import com.wiimedia.service.TicketRepositorySolr;
import com.wiimedia.utils.GetRandomStr;
import com.wiimedia.utils.SignatureBean;
import com.wiimedia.utils.weixin.WeixinUtil;
/**
 * 
 * 
 *<p>Project:mryl_phone_v2</p> 
 * 
 *<p>Package:com.wiimedia.controller</p> 
 * 
 *<p>Description:微信分享Controller</p>
 *
 *<p>Company:Wiimedia</p>
 *
 *@Athor:SongJia
 *
 *@Date:2016-7-15 上午09:34:10
 *
 */
 
@Controller
@RequestMapping("/WeixinshareController/Api/Inteface")
public class WeixinshareController {
 @Autowired
 private TicketRepositorySolr ticketRepositorySolr;
 
 @RequestMapping("/getSignature")
 public String getSignature( HttpServletRequest request,
   HttpServletResponse response) throws IOException, ParseException{
  //获取签名页面链接
  String url = request.getParameter("url");
  SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  //从数据库中获取标签,并检查标签是否过期
  Ticket oldticket = ticketRepositorySolr.getTicketById("20160114wiimediamrylsong1152");
  if(oldticket==null){//第一次访问,标签不存在。
   executeTicket(response,"1",url,format);
   return null;
  }else{//标签存在,判断标签是否超时
   String oldAcquiretime = oldticket.getAcquiretime();
   long difference=format.parse(format.format(new Date())).getTime()-format.parse(oldAcquiretime).getTime();
   if(difference>7100000){//标签超时,重新到微信服务器请求标签超时时间为7200秒(7200000毫秒)
    executeTicket(response,"2",url,format);
    return null; 
   }else{//标签未超时
    /** 
     * 注意事项           
     * 1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。
     * 2.签名用的url必须是调用JS接口页面的完整URL。      
     * 3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。 
     * 
     ****根据第1点要求   signature 配置的时候很容易出错,需要把生成 Ticket的 noncestr和 timestamp传给客户端***
     */
    String signature = signature(oldticket.getTicket(),oldticket.getTimestamp(),oldticket.getNoncestr(),url);
    SignatureBean signatureBean = new SignatureBean();
    signatureBean.setNoncestr(oldticket.getNoncestr());
    signatureBean.setSignature(signature);
    signatureBean.setTimestamp(oldticket.getTimestamp());
    signatureBean.setUrl(url);
    response.setContentType("text/html;charset=UTF-8");
    response.getWriter().print(new Gson().toJson(signatureBean));
    return null; 
   } 
  }
 
 
 }
 /**
  * 
  *<p>Project:mryl_phone_v2</p> 
  * 
  *<p>:mryl_phone_v2</p> 
  * 
  *<p>Description:更新和获取ticket的方法,因为用的solr所以更新和新增是一样的ID无则添加,有责更新</p>
  *
  *<p>Company:Wiimedia</p>
  *
  *@Athor:SongJia
  *
  *@Date:2016-7-15 上午09:45:00 
  *
  */
 public void executeTicket(HttpServletResponse response,String flag,String url,SimpleDateFormat format) throws IOException{
 
  //获取签名随即字符串
  GetRandomStr randomStr = new GetRandomStr();
  String noncestr = randomStr.getRandomString(15);
  //获取签名时间戳
  String timestamp = Long.toString(System.currentTimeMillis());
  //请求accessToken
  String accessTokenUrl ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=您的APPID&secret=您的密匙";
  String tokenJson = WeixinUtil.httpRequest(accessTokenUrl, "GET", null);
  Gson gson = new Gson();
  ShareAccess_Token token = gson.fromJson(tokenJson, ShareAccess_Token.class);
  String to= token.getAccess_token();
  //获取标签
  String urlTicket ="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+to+"&type=jsapi";
  String ticketJson = WeixinUtil.httpRequest(urlTicket, "GET", null);
  Ticket ticket = gson.fromJson(ticketJson, Ticket.class);
  String t = ticket.getTicket();
  //String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");
  //我的Ticket ID是写死的
  String acquiretime = format.format(new Date());
  ticket.setTid("20160114wiimediamrylsong1152");
  ticket.setAcquiretime(acquiretime);
  ticket.setTimestamp(timestamp);
  ticket.setNoncestr(noncestr);
  //因为用的SOLR所以更新和添加的方法是一样的,可以根据自己具体需求进行修改,本文不再贴出代码.
  if(flag.equals("2")){
   ticketRepositorySolr.addTicketToSolr(ticket); 
  }else{
   ticketRepositorySolr.addTicketToSolr(ticket);
  }
  /** 
   * 注意事项           
   * 1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。
   * 2.签名用的url必须是调用JS接口页面的完整URL。      
   * 3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。 
   * 
   *根据第1点要求   signature 配置的时候很容易出错,需要把生成 Ticket的 noncestr和 timestamp传给客户端*
   */
  String signature = signature(t,timestamp,noncestr,url);
  SignatureBean signatureBean = new SignatureBean();
  signatureBean.setNoncestr(noncestr);
  signatureBean.setSignature(signature);
  signatureBean.setTimestamp(timestamp);
  signatureBean.setUrl(url);
  response.setContentType("text/html;charset=UTF-8");
  response.getWriter().print(new Gson().toJson(signatureBean));
 }
 
 /**
  * 
  *<p>Project:mryl_phone_v2</p> 
  * 
  *<p>:mryl_phone_v2</p> 
  * 
  *<p>Description:根据标签,时间戳,密匙,URL进行签名</p>
  *
  *<p>Company:Wiimedia</p>
  *
  *@Athor:SongJia
  *
  *@Date:2016-7-15 上午09:37:13 
  *
  */
 private String signature(String jsapi_ticket, String timestamp, String noncestr, String url) {
  jsapi_ticket = "jsapi_ticket=" + jsapi_ticket;
  timestamp = "timestamp=" + timestamp;
  noncestr = "noncestr=" + noncestr;
  url = "url=" + url;
  String[] arr = new String[] { jsapi_ticket, timestamp, noncestr, url };
  // 将token、timestamp、nonce,url参数进行字典序排序
  Arrays.sort(arr);
  StringBuilder content = new StringBuilder();
  for (int i = 0; i < arr.length; i++) {
   content.append(arr[i]);
   if (i != arr.length - 1) {
    content.append("&");
   }
  }
  MessageDigest md = null;
  String tmpStr = null;
 
  try {
   md = MessageDigest.getInstance("SHA-1");
   // 将三个参数字符串拼接成一个字符串进行sha1加密
   byte[] digest = md.digest(content.toString().getBytes());
   tmpStr = byteToStr(digest);
  } catch (NoSuchAlgorithmException e) {
   e.printStackTrace();
  }
 
  content = null;
  return tmpStr;
 }
 /**
  * 将字节转换为十六进制字符串
  * 
  * @param mByte
  * @return
  */
 private static String byteToHexStr(byte mByte) {
 
  char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
  char[] tempArr = new char[2];
  tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
  tempArr[1] = Digit[mByte & 0X0F];
 
  String s = new String(tempArr);
  return s;
 }
 /**
  * 将字节数组转换为十六进制字符串
  * 
  * @param byteArray
  * @return
  */
 private static String byteToStr(byte[] byteArray) {
  String strDigest = "";
  for (int i = 0; i < byteArray.length; i++) {
   strDigest += byteToHexStr(byteArray[i]);
  }
  return strDigest;
 }
 
 
 class ShareAccess_Token{
  private String access_token;
  private String expires_in;
  public String getAccess_token() {
   return access_token;
  }
  public void setAccess_token(String accessToken) {
   access_token = accessToken;
  }
  public String getExpires_in() {
   return expires_in;
  }
  public void setExpires_in(String expiresIn) {
   expires_in = expiresIn;
  }
 
 }
}
二、客户端代码.

<script type="text/javascript">
   var url = window.location.href;
   var articleId = "";
   var shareTitle="明日医疗资讯";
   var shareImgUrl="";
   var userinfo = localStorage.getItem("_userinfo");
   var timestamp;
   var noncestr;
   var signature;
   //获取签名
    $.ajax({
      type: "GET",
      url: "WeixinshareController/Api/Inteface/getSignature",
      //data:{timestamp:timestamp,noncestr:noncestr,url:url},
      data:{url:url},
      success: function(data){
        var objData=JSON.parse(data); 
        timestamp=objData.timestamp; 
        noncestr=objData.noncestr; 
        signature=objData.signature;
         console.log(objData);
         wxShare();
      }
     });
   function wxShare(){
   wx.config({
    debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    appId: '您的appid', // 和获取Ticke的必须一样------必填,公众号的唯一标识
    timestamp:timestamp, // 必填,生成签名的时间戳
    nonceStr: noncestr, // 必填,生成签名的随机串
    signature: signature,// 必填,签名,见附录1
    jsApiList: [
    'onMenuShareAppMessage'
    ] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
   });
   }
   wx.ready(function(){
     //config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,
     //config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关
     //接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
 
    //----------“分享给朋友”
    wx.onMenuShareAppMessage({
     title: "明日医疗资讯", // 分享标题
     desc: shareTitle, // 分享描述
     link: url, // 分享链接
     imgUrl: shareImgUrl, // 分享图标
     type: '', // 分享类型,music、video或link,不填默认为link
     dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
     success: function () { 
      // 用户确认分享后执行的回调函数、
     },
     cancel: function () { 
      // 用户取消分享后执行的回调函数
     }
    });
    //------------"分享到朋友圈"
    wx.onMenuShareTimeline({
     title: '明日医疗资讯', // 分享标题
     link: '', // 分享链接
     imgUrl: shareImgUrl, // 分享图标
     success: function () { 
      // 用户确认分享后执行的回调函数
     },
     cancel: function () { 
      // 用户取消分享后执行的回调函数
     }
    });
    //-------------分享到QQ
    wx.onMenuShareQQ({
     title: '明日医疗资讯', // 分享标题
     desc: shareTitle, // 分享描述
     link: '', // 分享链接
     imgUrl: shareImgUrl, // 分享图标
     success: function () { 
      // 用户确认分享后执行的回调函数
     },
     cancel: function () { 
      // 用户取消分享后执行的回调函数
     }
    });
    //-------------分享到QQ空间
    wx.onMenuShareQZone({
     title: '明日医疗资讯', // 分享标题
     desc: shareTitle, // 分享描述
     link: '', // 分享链接
     imgUrl: shareImgUrl, // 分享图标
     success: function () { 
      // 用户确认分享后执行的回调函数
     },
     cancel: function () { 
      // 用户取消分享后执行的回调函数
     }
    });
 
   });

三、服务器需要的工具类和Model

① Ticket

package com.wiimedia.model;
 
 
public class Ticket{
 private String tid;
 private String ticket; 
 private String errcode;
 private String errmsg; 
 private String expires_in;
 private String acquiretime;
 private String noncestr;
 private String timestamp;
 
 public Ticket(String tid, String ticket, String errcode, String errmsg,
   String expiresIn, String acquiretime, String noncestr,
   String timestamp) {
  super();
  this.tid = tid;
  this.ticket = ticket;
  this.errcode = errcode;
  this.errmsg = errmsg;
  expires_in = expiresIn;
  this.acquiretime = acquiretime;
  this.noncestr = noncestr;
  this.timestamp = timestamp;
 }
 public String getTid() {
  return tid;
 }
 public void setTid(String tid) {
  this.tid = tid;
 }
 public String getTicket() {
  return ticket;
 }
 public void setTicket(String ticket) {
  this.ticket = ticket;
 }
 public String getErrcode() {
  return errcode;
 }
 public void setErrcode(String errcode) {
  this.errcode = errcode;
 }
 public String getErrmsg() {
  return errmsg;
 }
 public void setErrmsg(String errmsg) {
  this.errmsg = errmsg;
 }
 public String getExpires_in() {
  return expires_in;
 }
 public void setExpires_in(String expiresIn) {
  expires_in = expiresIn;
 }
 public String getAcquiretime() {
  return acquiretime;
 }
 public void setAcquiretime(String acquiretime) {
  this.acquiretime = acquiretime;
 }
 public String getNoncestr() {
  return noncestr;
 }
 public void setNoncestr(String noncestr) {
  this.noncestr = noncestr;
 }
 public String getTimestamp() {
  return timestamp;
 }
 public void setTimestamp(String timestamp) {
  this.timestamp = timestamp;
 }
 
 
}
② 添加到数据库的业务根据自己需要进行实现. 
③ GetRandomStr

package com.wiimedia.utils;
 
import java.util.Random;
 
public class GetRandomStr {
 /**
  * 
  *<p>Project:mryl_phone_v2</p> 
  * 
  *<p>:mryl_phone_v2</p> 
  * 
  *<p>Description:生成随即字符串 </p>
  *
  *<p>Company:Wiimedia</p>
  *
  *@Athor:SongJia
  *
  *@Date:2016-7-14 上午11:14:46 
  *
  */
 public String getRandomString(int length) {
  String base = "abcdefghijklmnopqrstuvwxyz0123456789"; 
  Random random = new Random(); 
  StringBuffer sb = new StringBuffer(); 
  for (int i = 0; i < length; i++) { 
   int number = random.nextInt(base.length()); 
   sb.append(base.charAt(number)); 
  } 
  return sb.toString(); 
  }
}
④ SignatureBean
package com.wiimedia.utils;
 
public class SignatureBean {
 private String noncestr;
 private String url;
 private String timestamp;
 private String signature;
 public String getNoncestr() {
  return noncestr;
 }
 public void setNoncestr(String noncestr) {
  this.noncestr = noncestr;
 }
 public String getUrl() {
  return url;
 }
 public void setUrl(String url) {
  this.url = url;
 }
 public String getTimestamp() {
  return timestamp;
 }
 public void setTimestamp(String timestamp) {
  this.timestamp = timestamp;
 }
 public String getSignature() {
  return signature;
 }
 public void setSignature(String signature) {
  this.signature = signature;
 }
 
}
⑤ WeixinUtil
package com.wiimedia.utils.weixin;
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 javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
 
/**
  * 
  *<p>Project:mryl_phone_v2</p> 
  * 
  *<p>:mryl_phone_v2</p> 
  * 
  *<p>Description:公众平台接口工具类</p>
  *
  *<p>Company:Wiimedia</p>
  *
  *@Athor:SongJia
  *
  *@Date:2016-7-15 上午09:37:13 
  *
  */
public class WeixinUtil {
 
 /** 
  * 发起https请求并获取结果 
  * 
  * @param requestUrl 请求地址 
  * @param requestMethod 请求方式(GET、POST) 
  * @param outputStr 提交的数据 
  * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值) 
  */
 public static String httpRequest(String requestUrl, String requestMethod, String outputStr) { 
 
  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(); 
   return buffer.toString(); 
  } catch (ConnectException ce) { 
   ce.printStackTrace(); 
  } catch (Exception e) { 
   e.printStackTrace();
  } 
  return ""; 
 }
}

四、至此,分享功能已经开发完成,但是,在生成signature的时候会遇到很多问题,这里提供一些wx.config失败的排错方法。

① 确认自己的生成的signature是否正确 
在微信提供的http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign进行校验

② wx.config中使用的noncestr, timestamp与用以签名中的对应noncestr, timestamp是否一致一致…如上面(一.服务器代码) 
(有可能因为JS页面加载顺序问题,服务器生成的signature,noncestr,timestamp在wx.config中没有获取到)。

③ 确认url是页面完整的url,包括GET参数部分 
需要去掉#后面的部分

④ config 中的 appid 与用来获取 jsapi_ticket 的 appid 是否一致

⑤ 报错{errmsg:config:ok}是debug的正常返回把调试模式关掉就OK 
wx.config debug: false, 

以上就是本文的全部内容,希望对大家的学习有所帮助


喜欢我的文章的,可以关注微信公众号“测试项目开发”,需要什么内容可以在里面提,我看到后会给大家解答。

2018-12-02 18:06:46 liunianheliu 阅读数 1195
  • 微信公众平台深度开发Java版v2.0单品课程——微信WEB...

    “微信公众平台深度开发Java版 v2.0”系列课程共有6季,使用JAVA语言,系统讲解微信公众平台订阅号、服务号官方列出的全部功能接口,包括:自定义菜单、个性化菜单(按需定制菜单)、群发消息、客服消息(有限次消息推送)、模板消息接(无限次消息推送)、微信网页开发(微信WEB开发、微信游戏)、微信JSSDK开发、用户管理、获取用户基本信息、网页授权获取用户基本信息(通过WEB得到用户信息)、二维码(临时二维码、永久二维码)、事件推送、接收普通消息、被动回复用户消息,等知识点。 以及,针对微信公众号开发的服务端架构设计方案。课程以微信公众号开发视角,讲解JAVA开发微信公众号所需的框架、第三方工具。 购买套餐还赠送经典微信开发课程——[微信公众号_独立知识点]环境搭建。该课程针对各种复杂的网络环境,讲解如何构建开发环境,已解决“没有服务器”、“没有固定IP”等开发者遇到的窘境。 课程采用独立知识点讲解,一个知识点,一组课程,真正做到“简单、高效、”以短的时间、实现的学习。更多课程信息请访问CSDN。网址:http://edu.csdn.net/lecturer/631 “微信公众平台企业号开发Java版”陆续上线。 详情 qq2326321088

    7574 人正在学习 去看看 翟东平

java版微信分享开发+ssm框架

简介

   本人第一次写csdn博客,表示这个编辑器第一次用户体验不是很好啊!不过功能还不错,日常吐槽一下。关于微信分享,微信官方文档给出了详细的开发流程,但是呢还是有许多坑,于是就想记录下来,方便自己回顾,也希望可以给看到的童鞋一些启发。

分享过程步骤简述


1.引入微信官方提供的JSSDK

  我们的项目是maven项目,可以直接在pom.xml添加项目依赖。如下
<!--微信SDK-->
<dependency>
  <groupId>com.github.wxpay</groupId>
  <artifactId>wxpay-sdk</artifactId>
  <version>0.0.3</version>
</dependency>

2.后台controller层开发

(1) 在前端页面加载的过程中,利用ajax从controller层获得需要向微信接口注入权限验证所需的配置信息。

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

根据配置信息,现在在后台将信息拼接好。

    @ResponseBody
    @RequestMapping("/coverAjax")
    public JSON coverAjax(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
        //微信分享开始
        String appId = Constants.APP_ID;//取项目中配置的公众号id
        String secret = Constants.APP_SECRET;//取项目中配置的公众号密钥
        String domainAddr = Constants.DOMAIN_URL;//项目中配置的网站的域名
        String strs = request.getParameter("memberId"); //前台传递的参数
        String url = request.getParameter("hurs");   //前台传递的参数
        Map<String, String> map = new HashMap<String, String>();
        map.put("appid", appId);
        map.put("secret", secret);
        map.put("url", url);        //这个地址是传给页面使用
        request.setAttribute("shareUrl", url);
        session.setAttribute("shareUrl", url);
        System.out.println(url);
        //开始微信分享链接签名
        Map<String, String> params = wxService.weixinjsIntefaceSign(map, session);
        request.setAttribute("params", params);
        session.setAttribute("params", params);
        System.out.println(params);
        params.put("shareUrl", url);
        return (JSON) JSON.toJSON(params);
    }

(2)前端配置好微信的配置信息,同时调用微信分享朋友和朋友圈接口 (担心像我一样刚做项目的小白,我就把前端ajax页面全部提出来放在下面)

$(function(){
     var hurs = location.href.split('#')[0];
     $.ajax({
         type: "POST",
         url: "${pageContext.request.contextPath}/WxShare/coverAjax",
         data: {
             "hurs":hurs
         },//传递到后台中的参数
         dataType: "json",
         cache: false,
         success: function (data) {
             wxConfig(data);
         },
         error:function () {
             alert("错误")
         }
     });
 });
 function wxConfig(data) {
     wx.config({
          debug: false, // 开启调试模式
          appId: data.appId, // 必填,公众号的唯一标识
          timestamp: data.timestamp, // 必填,生成签名的时间戳
         nonceStr: data.noncestr, // 必填,生成签名的随机串
         signature: data.signature,// 必填,签名,见附录1
        jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage']// 必填,需要使用的JS接口列表,所有JS接口列表见附录2
   });
    wx.ready(function () {
            wx.onMenuShareAppMessage({
            title: '${memberInfo.nickname}',
            desc: '${memberInfo.reInfo}',
            link: data.shareUrl,
            imgUrl: '${memberInfo.headImg}',
            success: function () {
                //alert("分享朋友成功");
            }
        });
        wx.onMenuShareTimeline({
            title: '${memberInfo.nickname}',
            link: data.shareUrl,
            imgUrl: '${memberInfo.headImg}',
            success: function () {
                //alert("分享朋友圈成功");
            }
        });
    });
}

注:分享的信息,title 和link 等信息 是在页面的后台包装好放在model里传给前端。

3.微信分享所需的签名算法

public Map<String, String> weixinjsIntefaceSign(Map<String, String> map,HttpSession session){
        //查看缓存数据是否存在
        Object cacheAccess_token = session.getAttribute("access_token_hl");
        Object cacheTicket = session.getAttribute("ticket");
        String cacheAccess_token_hl = (String) cacheAccess_token;
        String cacheTicket_hl = (String) cacheTicket;
        //取出来为空的话则说明cacheAccess_token缓存过期,重新获取
        if(null == cacheAccess_token_hl||cacheAccess_token_hl==""){
            //获取cacheAccess_token
            StringBuffer buffer = new StringBuffer();
            buffer.append("https://api.weixin.qq.com/cgi-bin/token?");
            buffer.append("grant_type=client_credential");
            buffer.append("&appid="+map.get("appid"));
            buffer.append("&secret="+map.get("secret"));
            System.out.println(buffer);
            try {
                String resultMsg = WxShareUtil.sendGet(buffer.toString(), "UTF-8");
                String jsons = resultMsg.substring(resultMsg.indexOf("{"), resultMsg.lastIndexOf("}") + 1);
                String[] result = jsons.split(",");
                String[] str1 = result[0].split(":");
                String str2 = str1[1].replace("\"","");
                cacheAccess_token_hl = str2;
                session.setAttribute("access_token_hl",cacheAccess_token_hl);
                         } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //取出来为空的话则说明cacheTicket缓存过期,重新获取
        if(null == cacheTicket_hl||cacheTicket_hl==""){
            ////获得jsapi_ticket
            StringBuffer buffer = new StringBuffer();
            buffer.append("https://api.weixin.qq.com/cgi-bin/ticket/getticket?");
            buffer.append("type=jsapi");
            buffer.append("&access_token="+session.getAttribute("access_token_hl"));
            String ticket = null;
            try {
                ticket = WxShareUtil.sendGet(buffer.toString(), "UTF-8");
                String jsons_t = ticket.substring(ticket.indexOf("{"), ticket.lastIndexOf("}") + 1);
                String[] result = jsons_t.split(",");
                String[] str1 = result[2].split(":");
                String str2 = str1[1].replace("\"","");
                cacheTicket_hl = str2;
               session.setAttribute("ticket",cacheTicket_hl);
                System.out.println("重新获取cacheTicket");
                System.out.println(ticket);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //生成签名
        SortedMap<Object,Object> params = new TreeMap<Object,Object>();
        params.put("timestamp", Long.toString(new Date().getTime()/1000));
        params.put("noncestr", WXPayUtil.generateNonceStr());
        params.put("jsapi_ticket",cacheTicket_hl);
        params.put("url",map.get("url"));//url地址
        StringBuffer sb = new StringBuffer();
        Set es = params.entrySet();
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            Object v = entry.getValue();
            sb.append(k + "=" + v + "&");
        }
        String  signStr = sb.toString().substring(0, sb.toString().length()-1);
        String sign = WxShareUtil.getSha1Sign(signStr);//签名
        Map<String, String> result = new HashMap<String,String>();
        result.put("timestamp",(String)params.get("timestamp"));
        result.put("noncestr", (String)params.get("noncestr"));
        result.put("signature", sign);
        result.put("appId",map.get("appid"));
        session.setMaxInactiveInterval(7200);
        return result;
    }

4.开发所需的工具类和遇到的坑


(1)常量类 Constants 就不展示出来了。分享的工具类代码如下: WxShareUtil

public class WxShareUtil {

    public static String sendGet(String url,String charset) throws IOException {
        //新建客户端
        HttpClient httpclient = new HttpClient();
        GetMethod getMethod = new GetMethod(url);
        httpclient.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, charset);
        httpclient.executeMethod(getMethod);
        String responseMsg = getMethod.getResponseBodyAsString();
        return responseMsg;
    }

    public static String getSha1Sign(String decript) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-1");
            try {
                digest.update(decript.getBytes("UTF-8"));
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            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) {
            e.printStackTrace();
        }
        return "";
    }
}

(2)开发微信分享遇到的坑
第一,微信分享,你需要将域名添加到微信公众平台上
JS接口安全域名
第二,你要将服务器的ip添加在微信公众平台的ip白名单里。
第三,分享页面的url,最好是动态获取当前页面的url,在传给后台,但是传的时候要做处理,使用 location.href.split(’#’)[0];去除多次分享后面的参数。
第四,在测试分享功能时,可以开启调试功能,然后在前端确保所需的配置参数都有。
第五,一定要将分享签名的token 和ticket 放在缓存里,微信是设置2个小时失效,所以 你需要设置两小时后 缓存清理,重新向微信申请获得access_token,注意,这个token和授权登录的token不一样。
最后:签名错误经常会碰到,耐心检查 一定会成功的。

5.写给大家和自己

感谢曾经写过微信分享的各位大佬,本人就是一个java后台开发菜鸟,基本都是面向百度编程,感谢他们的分享,让我顺利完成微信分享功能,如果代码中有一些很冗余甚至很智障的命名,请多担待认可,然后,如果有不是很懂的,也可以发邮件给我,我们可以talk一哈,以后,我还会继续写微信授权登录的java版功能开发和微信支付开发等等 ,希望自己能够逐渐成长为我很羡慕的大佬,加油!!!
最后,附上我的邮箱 3231394682@qq.com

java调用微信分享

阅读数 4264

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