java使用struts2框架开发微信服务号

来源:互联网 发布:linux防御cc攻击 编辑:程序博客网 时间:2024/06/15 00:40

一、前言

此文章仅代表个人的见解

二、微信接入

1、登录微信公众平台官网后,在公众平台后台管理页面 -开发者中心页,点击“修改配置”按钮,填写服务器地址(URL)、TokenEncodingAESKey,其中URL是开发者用来接收微信消息和事件的接口URLToken可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)。EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。

同时,开发者可选择消息加解密方式:明文模式、兼容模式和安全模式。模式的选择与服务器配置在提交后都会立即生效,请开发者谨慎填写及选择。加解密方式的默认状态为明文模式,选择兼容模式和安全模式需要提前配置好相关加解密代码,详情请参考消息体签名及加解密部分的文档

2、开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带四个参数:signature(微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数),timestamp(时间戳),nonce(随机数),echostr(随机字符串)。

开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。

    加密/校验流程如下:

    1. tokentimestampnonce三个参数进行字典序排序

    2. 将三个参数字符串拼接成一个字符串进行sha1加密

    3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

参考api地址http://mp.weixin.qq.com/wiki/17/2d4265491f12608cd170a95559800f2d.html

3、根据api第三步:依据接口文档实现业务逻辑

验证URL有效性成功后即接入生效,成为开发者。如果公众号类型为服务号(订阅号只能使用普通消息接口),可以在公众平台网站中申请认证,认证成功的服务号将获得众多接口权限,以满足开发者需求。

当你前面两部都搞定了的话,第三步就容易许多,基本根据api接口来就行了

三、进入代码开发

1、在上述的3步中,最为开始的疑问想必就是第二步了

当你在修改配置跟填写你自定义的token后,你所提交的地址必须就已经存在,并且能够接收微信推送过去的数据,同时进行验证,通过检验signature对请求进行校验,若校验成功则原样返回echostr,否则连提交保存都无法成功。

那么具体流程是这样子的,当你提交的时候,微信就发送了4个字段到你填写的url

下面是我接收这四个字段的代码,由于微信二次开发在网上对struts的框架介绍比较少,故此处就介绍struts2跟微信对接,而不使用servletservlet虽然是个好东西,笔者比较少用,其实struts2也是基于servlet的,所以基本都看得懂。简便写上接收代码,假设下面代码是填写url访问后的处理逻辑

  //打印接收的四个字段

 System.out.println("signature:" + request.getParameter("signature"));
 System.out.println("timestamp:" + request.getParameter("timestamp"));
 System.out.println("nonce:" + request.getParameter("nonce"));
 System.out.println("echostr:" + request.getParameter("echostr"));

// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败

  if (SignUtil.checkSignature(signature,timestamp,nonce)) {

         response.setContentType("text/html;charset=UTF-8");

         response.getWriter().write(echostr);

  }

 return null;

SignUtil工具类如下,主要改了token,与公众平台上填写的要一致

package cn.com.aguang.weixin.utils;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;/** * 请求校验工具类 *  * @author aguang * @Email zhaoguiguang@yeah.net *  */public class SignUtil {// 与接口配置信息中的Token要一致private static String token = "aguang";/** * 验证签名 *  * @param signature * @param timestamp * @param nonce * @return */public static boolean checkSignature(String signature, String timestamp,String nonce) {String[] arr = new String[] { token, timestamp, nonce };// 将token、timestamp、nonce三个参数进行字典序排序sort(arr);StringBuilder content = new StringBuilder();for (int i = 0; i < arr.length; i++) {content.append(arr[i]);}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;// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;}/** * 将字节数组转换为十六进制字符串 *  * @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;}public static void sort(String a[]) {for (int i = 0; i < a.length - 1; i++) {for (int j = i + 1; j < a.length; j++) {if (a[j].compareTo(a[i]) < 0) {String temp = a[i];a[i] = a[j];a[j] = temp;}}}}}
2、上述搭建后便可以进行提交了,如果保存成功,那么恭喜你,现在你可以进行第三步接口的开发了。

     废话我就不多说了直接上代码,大家可以根据api文档,还有我以下附上的几个封装好的处理工具,以及对接的代码和测试类代码,基本根据参考这几个,也可以进行微信接口开发了,我推荐大家先自己摸索一下,如果有什么地方卡住了可以下面的代码,如果有什么不规范的地方或者写得不好的地方,可以留言说一下,下面的例子只针对几个接口做开发而已,具体后面的功能再加上。不对的地方请指出

3、笔者填写的地址处理类(框架s2sh,大家可以根据各自不同情况进行修改)

package cn.com.aguang.weixin.action.weixin;import java.io.InputStream;import java.net.URLEncoder;import java.util.Date;import java.util.List;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import net.sf.json.JSONArray;import net.sf.json.JSONObject;import org.apache.struts2.ServletActionContext;import org.dom4j.Document;import org.dom4j.DocumentHelper;import org.dom4j.Element;import org.springframework.context.annotation.Scope;import org.springframework.stereotype.Controller;import cn.com.aguang.weixin.bussiness.WeixinOpenIdBus;import cn.com.aguang.weixin.bussiness.WeixinUtilBus;import cn.com.aguang.weixin.dao.WeixinOpenIdDAO;import cn.com.aguang.weixin.po.AgWeixinOpenId;import cn.com.aguang.weixin.utils.HttpUtils;import cn.com.aguang.weixin.utils.SignUtil;import cn.com.aguang.weixin.utils.WeiXinContants;import com.opensymphony.xwork2.ActionSupport;@Controller@Scope("prototype")public class AguangWeiXinSoftAction extends ActionSupport {// 微信opendId bus 类private WeixinOpenIdBus weixinOpenIdBus;// 为了维护行更高,觉得抽取业务逻辑的工具类,将业务一块一块分开private WeixinUtilBus weixinUtilBus;@Resourcepublic void setWeixinUtilBus(WeixinUtilBus weixinUtilBus) {this.weixinUtilBus = weixinUtilBus;}@Resourcepublic void setWeixinOpenIdBus(WeixinOpenIdBus weixinOpenIdBus) {this.weixinOpenIdBus = weixinOpenIdBus;}// 微信对接参数private String signature;// 微信对接参数private String timestamp;// 微信对接参数private String nonce;// 微信对接参数private String echostr;public String getSignature() {return signature;}public void setSignature(String signature) {this.signature = signature;}public String getTimestamp() {return timestamp;}public void setTimestamp(String timestamp) {this.timestamp = timestamp;}public String getNonce() {return nonce;}public void setNonce(String nonce) {this.nonce = nonce;}public String getEchostr() {return echostr;}public void setEchostr(String echostr) {this.echostr = echostr;}/** *  */private static final long serialVersionUID = 3674789318078769142L;/** * <p> * 重大发现,经过api多个接口的测试研究,发现很多接口都通过这里来认证,并且对接 * <p> *  * 微信接入,token暗号对接,对应公众号微信平台管理开发填写的地址 */@Overridepublic String execute() throws Exception {HttpServletResponse response = ServletActionContext.getResponse();// JSONArray array = new JSONArray();HttpServletRequest request = ServletActionContext.getRequest();// 验证消息的真实性接收的参数,即填写url时点击保存提交的时候,能接收到下面4个参数// System.out.println("signature:" + request.getParameter("signature"));// System.out.println("timestamp:" + request.getParameter("timestamp"));// System.out.println("nonce:" + request.getParameter("nonce"));// System.out.println("echostr:" + request.getParameter("echostr"));// 获取腾讯推送的数据流 经测试成功InputStream is = request.getInputStream();// 获取io流数据,如果是验证消息真实性即token,则不会有io流数据,否则一般为微信推送数据给我们,即有人关注了我们的公众号或者发送信息等推送事件String pushData = this.weixinUtilBus.getInfoFromIoStream(is);if (pushData != null && !"".equals(pushData)) {Document document = DocumentHelper.parseText(pushData);Element root = document.getRootElement();String msgType = root.element("MsgType").getText();if ("event".equals(msgType)) {// 事件 click、unSubscribe、subscribeString event = root.element("Event").getText();if ("click".equals(event.toLowerCase())) {// 点击按钮事件String eventKey = root.element("EventKey").getText();if ("aGuangSoftTest".equals(eventKey)) {// 点击者openIdString fromUserName = root.element("FromUserName").getText();// 接收的时间int createTime = Integer.parseInt(root.element("CreateTime").getText().toString());// 回复内容String content = WeiXinContants.AGUANG_SOFT_TEST_CLICK_CONTENT;// 生成微信接收格式的XML字符串String replyAguangSoftTestContentXMLStr = this.weixinUtilBus.getReplyConentXmlStr(content, fromUserName,createTime);// 响应回去,注意是以text/xml的形式返回response.setContentType("text/xml;charset=UTF-8");response.getWriter().write(replyAguangSoftTestContentXMLStr);}} else if ("unsubscribe".equals(event.toLowerCase())) {// 取消关注事件// 取消者openIdString fromUserName = root.element("FromUserName").getText();// 将该记录查出,然后删除List<AgWeixinOpenId> list = this.weixinOpenIdBus.findByProperty(WeixinOpenIdDAO.OPEN_ID,fromUserName);if (list != null && list.size() > 0) {this.weixinOpenIdBus.delete(list.get(0).getId());}} else if ("subscribe".equals(event.toLowerCase())) {// 关注事件// 关注者openIdString fromUserName = root.element("FromUserName").getText();AgWeixinOpenId woi = new AgWeixinOpenId();woi.setCreateTime(new Date());woi.setOpenId(fromUserName);this.weixinOpenIdBus.save(woi);} else if ("scancode_waitmsg".equals(event)) {String scanResult = root.element("ScanCodeInfo").element("ScanResult").getText();int createTime = Integer.parseInt(root.element("CreateTime").getText());// 点击扫描者String fromUserName = root.element("FromUserName").getText();// 生成XML字符串数据String scancodeResultXMLStr = this.weixinUtilBus.scancodeResultXMLStr(scanResult, fromUserName,createTime);// 响应回去response.setContentType("text/xml;charset=UTF-8");response.getWriter().write(scancodeResultXMLStr);}} else if ("text".equals(msgType)) {// 接收普通消息信息内容// 发送消息者openIdString fromUserName = root.element("FromUserName").getText();// 接收的时间int createTime = Integer.parseInt(root.element("CreateTime").getText().toString());// 接收的消息String content = root.element("Content").getText();Element weather = this.getWeatherInfoByCity(content);if (weather == null) {System.out.println("输入城市名可以查询当天的天气预报情况,如:广州");// 生成微信接收格式的XML字符串String replyAguangSoftTestContentXMLStr = this.weixinUtilBus.getReplyConentXmlStr("输入城市名可以查询当天的天气预报情况,如:广州",fromUserName, createTime);// 响应回去,注意是以text/xml的形式返回response.setContentType("text/xml;charset=UTF-8");response.getWriter().write(replyAguangSoftTestContentXMLStr);} else {// 回复内容String weatherInfo = "您输入的城市为:"+ weather.element("city").getText() + ",天气:"+ weather.element("status1").getText() + "->"+ weather.element("status2").getText() + "。风向:"+ weather.element("direction1").getText()+ "。紫外线指数:"+ weather.element("pollution").getText() + "。温度:"+ weather.element("ssd_l").getText() + "。注意事项:"+ weather.element("gm_s").getText() + "。"+ weather.element("yd_s").getText() + "。"+ weather.element("xcz_s").getText() + "。"+ weather.element("pollution_s").getText();System.out.println(weatherInfo);// 生成微信接收格式的XML字符串String replyAguangSoftTestContentXMLStr = this.weixinUtilBus.getReplyConentXmlStr(weatherInfo, fromUserName,createTime);// 响应回去,注意是以text/xml的形式返回response.setContentType("text/xml;charset=UTF-8");response.getWriter().write(replyAguangSoftTestContentXMLStr);}}}is.close();// 如果这3个参数有为空的,即不是验证消息的真实性,那么就返回空,因为是处理上面的其他时间,下面的验证无需执行if (signature == null || timestamp == null || nonce == null) {return null;}// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败if (SignUtil.checkSignature(signature, timestamp, nonce)) {response.setContentType("text/html;charset=UTF-8");response.getWriter().write(echostr);}return null;}/** * <b>获取最新accessToken<b> *  * @return String */public String getAccessToken() {String apiUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type="+ WeiXinContants.GRANT_TYPE + "&appid=" + WeiXinContants.APP_ID+ "&secret=" + WeiXinContants.APP_SECRET;JSONObject json = JSONObject.fromObject(HttpUtils.getInstance().getJsonObjectByUrl(apiUrl));String access_token = json.get("access_token") + "";System.out.println("access_token:" + access_token);return access_token;}/** * 获取微信服务ip 列表 */public String getWeiXinServiceIp() {String apiUrl = "https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token="+ WeiXinContants.ACCESS_TOKEN;JSONObject json = JSONObject.fromObject(HttpUtils.getInstance().getJsonObjectByUrl(apiUrl));String ipStr = json.get("ip_list") + "";System.out.println(ipStr);return ipStr;}/** * 创建菜单 */public String createMenu() throws Exception {String apiUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token="+ new AguangWeiXinSoftAction().getAccessToken();// 推送给微信服务器的json数据JSONObject data = new JSONObject();// 按钮json数组数据JSONArray buttonJsonArray = new JSONArray();// 菜单按钮1JSONObject button1 = new JSONObject();button1.put("type", "click");button1.put("name", "普通按钮");button1.put("key", "aGuangSoftTest");// 添加按钮1buttonJsonArray.add(button1);// 菜单按钮2JSONObject button2 = new JSONObject();button2.put("name", "多级菜单");JSONArray sub_button2 = new JSONArray();// 菜单按钮2中的子按钮1JSONObject button2Sub1 = new JSONObject();// view表示点击后跳转链接button2Sub1.put("type", "view");button2Sub1.put("name", "谁的服务");button2Sub1.put("url", "http://www.aguang.cc/weixin/");// 菜单按钮2中的子按钮2JSONObject button2Sub2 = new JSONObject();// view表示点击后跳转链接button2Sub2.put("type", "view");button2Sub2.put("name", "我的信息");button2Sub2.put("url", "http://www.aguang.cc/weixin/wx/");// 菜单按钮2中的子按钮3JSONObject button2Sub3 = new JSONObject();// 表示点击后跳转链接button2Sub3.put("type", "view");button2Sub3.put("name", "赞一个");button2Sub3.put("url", "http://www.aguang.cc/weixin/wx/");sub_button2.add(button2Sub1);sub_button2.add(button2Sub2);sub_button2.add(button2Sub3);// 将子菜单绑定到菜单2按钮中button2.put("sub_button", sub_button2.toString());// 添加菜单按钮2buttonJsonArray.add(button2);// 菜单按钮3JSONObject button3 = new JSONObject();button3.put("name", "最新按钮");// 定义菜单按钮3的子按钮json数组JSONArray sub_button3 = new JSONArray();// 菜单按钮3中的子按钮1JSONObject button3Sub1 = new JSONObject();button3Sub1.put("type", "scancode_waitmsg");button3Sub1.put("name", "扫码带提示");button3Sub1.put("key", "searchCode");button3Sub1.put("sub_button", "[]");// 将子按钮绑定到菜单按钮3中sub_button3.add(button3Sub1.toString());// 菜单按钮3中的子按钮2JSONObject button3Sub2 = new JSONObject();button3Sub2.put("type", "scancode_push");button3Sub2.put("name", "扫码推事件");button3Sub2.put("key", "searchCodePushEvent");button3Sub2.put("sub_button", "[]");sub_button3.add(button3Sub2.toString());button3.put("sub_button", sub_button3.toString());buttonJsonArray.add(button3);// 最终的按钮自定义菜单数据data.put("button", buttonJsonArray.toString());try {System.out.println(data.toString());HttpUtils.getInstance().createPostHttpRequest(apiUrl,data.toString());} catch (Exception e) {e.printStackTrace();}return null;}/** *  * <b>微信菜单2的多级菜单1<b> *  * @return String */public String menu2Button1() {return SUCCESS;}/** *  * <b>微信菜单2的多级菜单2<b> *  * @return String */public String menu2Button2() {return SUCCESS;}/** *  * <b>微信菜单2的多级菜单1<b> *  * @return String */public String menu2Button3() {return SUCCESS;}/** * <b>根据openId获取用户信息<b> *  * @return String */public String getUserInfoByOpenId() {String openId = "";String apiUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token="+ getAccessToken() + "&openid=" + openId + "&lang=zh_CN";try {HttpUtils.getInstance().createPostHttpRequest(apiUrl, "");} catch (Exception e) {e.printStackTrace();}return SUCCESS;}/** *  * <b>根据城市名获取该城市的天气预报<b> *  * @return String */private Element getWeatherInfoByCity(String city) {try {String apiUrl = "http://php.weather.sina.com.cn/xml.php?city="+ URLEncoder.encode(city, "gbk")+ "&password=DJOYnieT8234jlsK&day=0";String data = HttpUtils.getInstance().getXmlDataByUrl(apiUrl);System.out.println(data);Document document = DocumentHelper.parseText(data);Element root = document.getRootElement();// 事件 click、unSubscribe、subscribeElement weather = root.element("Weather");if (weather == null) {return null;} else {return weather;}} catch (Exception e) {// e.printStackTrace();System.out.println("转码失败");return null;}}}
4、action的测试类(自定义菜单主要是在测试类运行)

package cn.com.aguang.weixin.action.weixin;import java.net.URLEncoder;import java.util.Date;import net.sf.json.JSONArray;import net.sf.json.JSONObject;import org.dom4j.Document;import org.dom4j.DocumentHelper;import org.dom4j.Element;import org.junit.BeforeClass;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import cn.com.aguang.commons.business.BusinessException;import cn.com.aguang.weixin.bussiness.WeixinOpenIdBus;import cn.com.aguang.weixin.po.AgWeixinOpenId;import cn.com.aguang.weixin.utils.HttpUtils;import cn.com.aguang.weixin.utils.WeiXinContants;import com.opensymphony.xwork2.interceptor.annotations.After;import com.opensymphony.xwork2.interceptor.annotations.Before;public class AguangWeiXinSoftTest {private static WeixinOpenIdBus weixinOpenIdBus;/** * @throws java.lang.Exception */@BeforeClasspublic static void setUpBeforeClass() {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");weixinOpenIdBus = (WeixinOpenIdBus) ctx.getBean("weixinOpenIdBus");}/** * @throws java.lang.Exception */@Beforepublic void setUp() throws Exception {System.out.println("---------------Begin");}/** * @throws java.lang.Exception */@Afterpublic void tearDown() throws Exception {System.out.println("---------------End");}/** * 更新access_token */public void updateAccessToken() {String access_token = getAccessToken();System.out.println("更新之前的access_token:" + WeiXinContants.ACCESS_TOKEN);if (access_token != null && "".equals(access_token)) {WeiXinContants.setAccessToken(access_token);}System.out.println("更新之后的access_token:" + access_token);}/** * 获取微信服务ip 列表 */public void getWeiXinServiceIp() {String apiUrl = "https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token="+ WeiXinContants.ACCESS_TOKEN;JSONObject json = JSONObject.fromObject(HttpUtils.getInstance().getJsonObjectByUrl(apiUrl));String ipStr = json.get("ip_list") + "";System.out.println(ipStr);}/** * 创建自定义菜单 */public void createMenu() {String apiUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token="+ getAccessToken();// 推送给微信服务器的json数据JSONObject data = new JSONObject();// 按钮json数组数据JSONArray buttonJsonArray = new JSONArray();// 菜单按钮1JSONObject button1 = new JSONObject();button1.put("type", "click");button1.put("name", "普通按钮");button1.put("key", "aGuangSoftTest");// 添加按钮1buttonJsonArray.add(button1);// 菜单按钮2JSONObject button2 = new JSONObject();button2.put("name", "多级菜单");JSONArray sub_button2 = new JSONArray();// 菜单按钮2中的子按钮1JSONObject button2Sub1 = new JSONObject();// view表示点击后跳转链接button2Sub1.put("type", "view");button2Sub1.put("name", "谁的服务");button2Sub1.put("url","http://www.aguang.cc/weixin/wx/menu2Button1.action");// 菜单按钮2中的子按钮2JSONObject button2Sub2 = new JSONObject();// view表示点击后跳转链接button2Sub2.put("type", "view");button2Sub2.put("name", "我的信息");button2Sub2.put("url","http://www.aguang.cc/weixin/wx/menu2Button2.action");// 菜单按钮2中的子按钮3JSONObject button2Sub3 = new JSONObject();// 表示点击后跳转链接button2Sub3.put("type", "view");button2Sub3.put("name", "赞一个");button2Sub3.put("url","http://www.aguang.cc/weixin/wx/menu2Button3.action");sub_button2.add(button2Sub1);sub_button2.add(button2Sub2);sub_button2.add(button2Sub3);// 将子菜单绑定到菜单2按钮中button2.put("sub_button", sub_button2.toString());// 添加菜单按钮2buttonJsonArray.add(button2);// 菜单按钮3JSONObject button3 = new JSONObject();button3.put("name", "最新按钮");// 定义菜单按钮3的子按钮json数组JSONArray sub_button3 = new JSONArray();// 菜单按钮3中的子按钮1JSONObject button3Sub1 = new JSONObject();button3Sub1.put("type", "scancode_waitmsg");button3Sub1.put("name", "扫码带提示");button3Sub1.put("key", "searchCode");button3Sub1.put("sub_button", "[]");// 将子按钮绑定到菜单按钮3中sub_button3.add(button3Sub1.toString());// 菜单按钮3中的子按钮2JSONObject button3Sub2 = new JSONObject();button3Sub2.put("type", "scancode_push");button3Sub2.put("name", "扫码推事件");button3Sub2.put("key", "searchCodePushEvent");button3Sub2.put("sub_button", "[]");sub_button3.add(button3Sub2.toString());button3.put("sub_button", sub_button3.toString());buttonJsonArray.add(button3);// 最终的按钮自定义菜单数据data.put("button", buttonJsonArray.toString());try {System.out.println(data.toString());HttpUtils.getInstance().createPostHttpRequest(apiUrl,data.toString());} catch (Exception e) {e.printStackTrace();}}/** * <b>根据openId获取用户信息,需要认证才可以,暂时无法使用,还没有认证<b> *  * @return String */public void getUserInfoByOpenId() {String openId = "cJCc8GeFCJu8kHocFLqg";String apiUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token="+ getAccessToken() + "&openid=" + openId + "&lang=zh_CN";try {JSONObject json = JSONObject.fromObject(HttpUtils.getInstance().createPostHttpRequest(apiUrl, ""));System.out.println("国家" + json.get("country"));System.out.println("省份" + json.get("province"));System.out.println("城市" + json.get("city"));System.out.println("昵称" + json.get("nickname"));System.out.println("头像地址" + json.get("headimgurl"));} catch (Exception e) {e.printStackTrace();}}/** * <b>获取最新accessToken<b> *  * @return String */private String getAccessToken() {String apiUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type="+ WeiXinContants.GRANT_TYPE + "&appid=" + WeiXinContants.APP_ID+ "&secret=" + WeiXinContants.APP_SECRET;JSONObject json = JSONObject.fromObject(HttpUtils.getInstance().getJsonObjectByUrl(apiUrl));String access_token = json.get("access_token") + "";System.out.println("access_token:" + access_token);return access_token;}/** * 添加关注记录 */public void addWeixinOpenId() {AgWeixinOpenId woi = new AgWeixinOpenId();woi.setOpenId("123");woi.setCreateTime(new Date());try {weixinOpenIdBus.save(woi);} catch (BusinessException e) {e.printStackTrace();}}/** *  * 根据城市获取天气预报信息 *  * @param city */@Testpublic void getWeatherInfoByCity() {String city = "广州";try {String apiUrl = "http://php.weather.sina.com.cn/xml.php?city="+ URLEncoder.encode(city, "gbk")+ "&password=DJOYnieT8234jlsK&day=0";String data = HttpUtils.getInstance().getXmlDataByUrl(apiUrl);System.out.println(data);Document document = DocumentHelper.parseText(data);Element root = document.getRootElement();// 事件 click、unSubscribe、subscribeElement weather = root.element("Weather");if (weather == null) {System.out.println("无数据");} else {// 回复内容String weatherInfo = "您输入的城市为:"+ weather.element("city").getText() + "。天气:"+ weather.element("status1").getText() + "->"+ weather.element("status2").getText() + "。风向:"+ weather.element("direction1").getText() + "。紫外线指数:"+ weather.element("pollution").getText() + "。温度:"+ weather.element("ssd_l").getText() + "。注意事项:"+ weather.element("gm_s").getText() + "。"+ weather.element("yd_s").getText() + "。"+ weather.element("xcz_s").getText() + "。"+ weather.element("pollution_s").getText();System.out.println(weatherInfo);}} catch (Exception e) {// e.printStackTrace();System.out.println("转码失败");}}}

5、几个相关的bus类,直接上实现类就行了

package cn.com.aguang.weixin.bussiness.impl;import javax.annotation.Resource;import org.springframework.stereotype.Service;import cn.com.aguang.commons.business.AbstractBaseBus;import cn.com.aguang.commons.dao.BaseDAO;import cn.com.aguang.weixin.bussiness.WeixinOpenIdBus;import cn.com.aguang.weixin.dao.WeixinOpenIdDAO;import cn.com.aguang.weixin.po.AgWeixinOpenId;@Service("weixinOpenIdBus")public class WeixinOpenIdBusImpl extendsAbstractBaseBus<AgWeixinOpenId, String> implements WeixinOpenIdBus {private WeixinOpenIdDAO weixinOpenIdDAO;@Resourcepublic void setWeixinOpenIdDAO(WeixinOpenIdDAO weixinOpenIdDAO) {this.weixinOpenIdDAO = weixinOpenIdDAO;}@Overrideprotected BaseDAO<AgWeixinOpenId, String> getDAO() {return weixinOpenIdDAO;}@Overrideprotected void checkRequires(AgWeixinOpenId paramT) {// do nothing}}

package cn.com.aguang.weixin.bussiness.impl;import java.io.IOException;import java.io.InputStream;import org.springframework.stereotype.Service;import cn.com.aguang.weixin.bussiness.WeixinUtilBus;import cn.com.aguang.weixin.utils.WeiXinContants;@Service("weixinUtilBus")public class WeixinUtilBusImpl implements WeixinUtilBus {/** * 根据IO流获取IO流的数据 */@Overridepublic String getInfoFromIoStream(InputStream is) {String pushData = "";try {if (is != null) {String charSet = null;charSet = "utf-8";byte b[] = new byte[200];int numRead = is.read(b);if (numRead != -1) {String content = new String(b, 0, numRead);while (numRead != -1) {numRead = is.read(b);if (numRead != -1) {String newContent = new String(b, 0, numRead,charSet);content += newContent;}}pushData = content;System.out.println("接收到推送的信息:" + content);}}return pushData;} catch (IOException e) {return "";}}/** * 根据内容,接收者openId获取应该相应的XML字符串 */@Overridepublic String getReplyConentXmlStr(String content, String toUserName,int createTime) {String replyAguangSoftTestContentXMLStr = "<xml><ToUserName><![CDATA["+ toUserName+ "]]></ToUserName><FromUserName><![CDATA["+ WeiXinContants.WEI_XIN_NO+ "]]></FromUserName><CreateTime>"+ createTime+ "</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA["+ content + "]]></Content></xml>";return replyAguangSoftTestContentXMLStr;}/** * 根据扫描内容,接收者openId获取应该相应的XML字符串 */@Overridepublic String scancodeResultXMLStr(String scanResult, String toUserName,int createTime) {String scancodeResultXMLStr = "<xml><ToUserName><![CDATA["+ toUserName+ "]]></ToUserName><FromUserName><![CDATA["+ WeiXinContants.WEI_XIN_NO+ "]]></FromUserName><CreateTime>"+ createTime+ "</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA["+ scanResult + "]]></Content></xml>";return scancodeResultXMLStr;}}

6、相关的工具类,主要是请求封装的工具类,微信公众平台的appid等参数定义

package cn.com.aguang.weixin.utils;public class WeiXinContants {public static final String WEI_XIN_NO = "AGuangSoftTest"; //公众平台设置的微信号,发送信息时需要public static final String APP_ID = "**************";//根据自己的替换public static final String APP_SECRET = "*************************"; 根据自己的替换public static final String TOKEN = "***";  //根据自己的替换public static final String GRANT_TYPE = "client_credential";public static String ACCESS_TOKEN = "Cx_JPR-Cs791HRhDcA2jjm9U_UtHEPaOXT3q-LL7six96i9u0_-olxXoaHUbhGz3Cal7sp2C2KM0qerk4fpPnU7_iZCKhi42nOaKE2SA-Os";public static String AGUANG_SOFT_TEST_CLICK_CONTENT = "接口点击事件回复调试成功,这只是一小步";public static void setAccessToken(String access_token) {ACCESS_TOKEN = access_token;}}
封装的请求工具类

package cn.com.aguang.weixin.utils;import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.net.HttpURLConnection;import java.net.URL;import net.sf.json.JSONArray;import net.sf.json.JSONObject;/** *  * 饿汉试 单例 直接实例化一个,线程安全(以前一直用懒汉式,第一次调用实例化,但是有可能会出现多个实例,非常小的概率) *  * @author aGuang * */public class HttpUtils {private static HttpUtils instance = new HttpUtils();/** * 私有构造方法 */private HttpUtils() {}public static HttpUtils getInstance() {return instance;}/** * 根据api访问地址获取地址返回的json数据 *  * @param apiUrl *  * @return json */public String getJsonObjectByUrl(String apiUrl) {try {URL url = new URL(apiUrl);HttpURLConnection con = (HttpURLConnection) url.openConnection();con.setAllowUserInteraction(false);InputStream urlStream = url.openStream();String charSet = null;charSet = "utf-8";byte b[] = new byte[200];int numRead = urlStream.read(b);String content = new String(b, 0, numRead);while (numRead != -1) {numRead = urlStream.read(b);if (numRead != -1) {String newContent = new String(b, 0, numRead, charSet);content += newContent;}}// System.out.println("content:" + content);JSONObject json = JSONObject.fromObject(new String(content.getBytes(), "utf-8"));urlStream.close();return json.toString();} catch (Exception e) {e.printStackTrace();return null;}}/** * 根据api访问地址获取地址返回的json数据 *  * @param apiUrl *  * @return */public String getJsonArrayByUrl(String apiUrl) {try {URL url = new URL(apiUrl);HttpURLConnection con = (HttpURLConnection) url.openConnection();con.setAllowUserInteraction(false);InputStream urlStream = url.openStream();String charSet = null;charSet = "utf-8";byte b[] = new byte[200];int numRead = urlStream.read(b);String content = new String(b, 0, numRead);while (numRead != -1) {numRead = urlStream.read(b);if (numRead != -1) {String newContent = new String(b, 0, numRead, charSet);content += newContent;}}// System.out.println("content:" + content);JSONArray array = JSONArray.fromObject(new String(content.getBytes(), "utf-8"));urlStream.close();return array.toString();} catch (Exception e) {// e.printStackTrace();return null;}}/** * 根据api访问地址获取地址返回的XML数据 *  * @param apiUrl *  * @return */public String getXmlDataByUrl(String apiUrl) {try {URL url = new URL(apiUrl);HttpURLConnection con = (HttpURLConnection) url.openConnection();con.setAllowUserInteraction(false);InputStream urlStream = url.openStream();String charSet = null;charSet = "utf-8";byte b[] = new byte[6000];int numRead = urlStream.read(b);String content = new String(b, 0, numRead);while (numRead != -1) {numRead = urlStream.read(b);if (numRead != -1) {String newContent = new String(b, 0, numRead, charSet);content += newContent;}}// System.out.println("content:" + content);String xmlString = new String(content.getBytes(), "utf-8");urlStream.close();return xmlString;} catch (Exception e) {// e.printStackTrace();return null;}}/** * <b>用post请求访问一个http<b> *  * @param apiUrl *            请求地址 * @return */public String createPostHttpRequest(String apiUrl, String data)throws Exception {// Post请求的url,与get不同的是不需要带参数URL postUrl = new URL(apiUrl);// 打开连接HttpURLConnection connection = (HttpURLConnection) postUrl.openConnection();// 25秒连接超时connection.setConnectTimeout(25000);// 读取超时 --服务器响应比较慢,增大时间connection.setReadTimeout(25000);HttpURLConnection.setFollowRedirects(true);// 设置是否向connection输出,因为这个是post请求,参数要放在// http正文内,因此需要设为trueconnection.setDoOutput(true);// Read from the connection. Default is true.connection.setDoInput(true);// Set the post method. Default is GETconnection.setRequestMethod("POST");// Post cannot use caches// Post 请求不能使用缓存connection.setUseCaches(false);// URLConnection.setFollowRedirects是static函数,作用于所有的URLConnection对象。// connection.setFollowRedirects(true);// URLConnection.setInstanceFollowRedirects是成员函数,仅作用于当前函数connection.setInstanceFollowRedirects(true);// 配置本次连接的Content-type,配置为application/x-www-form-urlencoded的// 意思是正文是urlencoded编码过的form参数,下面我们可以看到我们对正文内容使用URLEncoder.encode// 进行编码// connection.setRequestProperty("Content-Type",// "application/x-www-form-urlencoded");connection.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0");connection.setRequestProperty("Referer", "https://api.weixin.qq.com/");// 连接,从postUrl.openConnection()至此的配置必须要在connect之前完成,// 要注意的是connection.getOutputStream会隐含的进行connect。connection.connect();OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());// The URL-encoded contend// 正文,正文内容其实跟get的URL中'?'后的参数字符串一致// String content = URLEncoder.encode("中国聚龙", "utf-8");String content = data;// DataOutputStream.writeBytes将字符串中的16位的unicode字符以8位的字符形式写道流里面out.write(content);out.flush();out.close(); // flush and closeBufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"));// 设置编码,否则中文乱码String line = "";System.out.println("=============================");System.out.println("Contents of post request");System.out.println("=============================");while ((line = reader.readLine()) != null) {// line = new String(line.getBytes(), "utf-8");System.out.println("返回的信息:" + line);}System.out.println("=============================");System.out.println("Contents of post request ends");System.out.println("=============================");reader.close();connection.disconnect();return "";}}

上面的几个代码就是我的公众号测试开发上写的,如果有什么不对的,请指出


有什么好的建议也可以给我,对于更多要了解微信的,我建议还是多看微信提供的api文档,本文只供参考,希望对大家有所帮助(其实主要是代码微笑,我个人觉得新手来说,代码就是王道,但是后面还有多数的接口使用,大家可以自己摸索使用)

0 0