微信公众号开发
第一章 公众号基本配置
1. 微信公众号与Java后台的基本连接配置
1.1 步骤:
1.1.1、填写服务器配置
1.1.2、验证服务器地址的有效性
1.1.3、依据接口文档实现业务逻辑
微信后台与Java后台的初始连接:
URL是Java后台服务器的url地址;
Token令牌是自己定义的,和Java后台获取连接是需要验证的。
1.2验证消息的确来自微信服务器
需验证参数如下:
1.2.1、Java后台需要通过get请求与微信服务器验证上述参数
1)将token、timestamp、nonce三个参数进行字典序排序
2)将三个参数字符串拼接成一个字符串进行sha1加密
3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
Java后台完成初始连接校验:
CheckUtil:
import java.security.MessageDigest; import java.util.Arrays; /** * 校验工具类 * @author li_hao * */ public class CheckUitl { private static final String token = "centit";//定义一个token,用来和微信端校验 /** * 校验方法 */ public static boolean checkSignature(String signature,String timestamp,String nonce){ String[] arr = new String[]{token,timestamp,nonce}; Arrays.sort(arr);//排序 //生成字符串 StringBuffer content = new StringBuffer(); for(int i=0;i<arr.length;i++){ content.append(arr[i]); } String temp = getSha1(content.toString());//调用sha1加密 return temp.equals(signature); } /** * sha1加密 */ public static String getSha1(String str){ if(str==null || str.length()==0){ return null; } char hexDigits[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; try { MessageDigest mdTemp = MessageDigest.getInstance("SHA1"); mdTemp.update(str.getBytes("UTF-8")); byte[] md = mdTemp.digest(); int j = md.length; char buf[] = new char[j*2]; int k = 0; for(int i=0;i<j;i++){ byte byte0 = md[i]; buf[k++] = hexDigits[byte0 >>> 4 & 0xf]; buf[k++] = hexDigits[byte0 & 0xf]; } return new String(buf); } catch (Exception e) { return null; } } }
WeixinServlet:
import java.io.IOException; import java.io.PrintWriter; import java.util.Date; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.dom4j.DocumentException; import com.centit.cmip.weixin.po.TextMessage; import com.centit.cmip.weixin.util.CheckUitl; import com.centit.cmip.weixin.util.MessageUtil; public class WeixinServlet extends HttpServlet { /** * 初始连接微信的信息校验 */ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取四个参数,进行验证 String signature = req.getParameter("signature");//微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 String timestamp = req.getParameter("timestamp");//时间戳 String nonce = req.getParameter("nonce");//随机数 String echostr = req.getParameter("echostr");//随机字符串 //调用校验方法进行校验,如果校验成功,就把随机字符串(echostr)返回 PrintWriter out = resp.getWriter(); if(CheckUitl.checkSignature(signature, timestamp, nonce)){ out.print(echostr); } } /** * 后台返回给微信 */ @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); resp.setCharacterEncoding("UTF-8"); PrintWriter out = resp.getWriter();//后台返回给微信页面的方法 try { Map<String, String> map = MessageUtil.xmlToMap(req);//获取消息到集合中 //获取参数 String toUserName = map.get("ToUserName"); String fromUserName = map.get("FromUserName"); String msgType = map.get("MsgType"); String content = map.get("Content"); String message = null; //根据msgType判断消息类型 if(MessageUtil.MESSAGE_EVENT.equals(msgType)){ //事件推送 String eventType = map.get("Event");//获取事件推送中的具体事件类型 if(MessageUtil.MESSAGE_SUBSCRIBE.equals(eventType)){ //关注 message = MessageUtil.initText(toUserName, fromUserName, MessageUtil.menuText());//关注推送 }else if(MessageUtil.MESSAGE_CLICK.equals(eventType)){ //click按钮点击回复 message = MessageUtil.initNewsMessage(toUserName, fromUserName); }else if(MessageUtil.MESSAGE_VIEW.equals(eventType)){ //view类型按钮 String url = map.get("EventKey");//得到url //message = MessageUtil.initText(toUserName, fromUserName, url);//回复一个url }else if(MessageUtil.MESSAGE_SCANCODE.equals(eventType)){ //扫码类型按钮 String key = map.get("EventKey"); //message = MessageUtil.initText(toUserName, fromUserName, key);//回复一个key值 } System.out.println(message); out.print(message); }else if(MessageUtil.MESSAGE_LOCATION.equals(msgType)){ //地理位置回复 String label = map.get("Label"); message = MessageUtil.initText(toUserName, fromUserName, label);//回复一个地理位置 System.out.println(message); out.print(message); } } catch (DocumentException e) { e.printStackTrace(); }finally{ out.close(); } } }
2. 获取access_token
access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
接口调用说明:
备注(个人理解):
如果说初始连接是设置的Token令牌是我们自己设置的初始连接秘钥,那么access_token则是我们在Java后台与微信后台的交互中微信返回的唯一凭证,几乎Java后台所有的操作(如:设置菜单按钮,设置回复,上传媒体消息,上传图文消息体,以及推送等等)都需要access_token来进行与微信服务器的验证,才能成功。
access_token有效期:7200秒;
access_token每天只能获取2000次;
(所以在Java后台需要把access_token获取保存,1:定时器;2:保存数据库)
/** * 获取Access_Token * @return */ public static AccessToken getAccessToken(String appid,String appsecret){ AccessToken token = new AccessToken(); String url = ACCESS_TOKEN_URL.replace("APPID", appid).replace("APPSECRET", appsecret);//替换原url中的APPID和APPSECRIPT JSONObject jsonObject = doGetStr(url);//通过get方法获取结果 if(jsonObject != null){ try { token.setToken(jsonObject.getString("access_token"));//放入access_token token.setExpiresIn(jsonObject.getInt("expires_in"));//放入有效时间 } catch (JSONException e) { token = null; // 获取token失败 log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); } } return token; }
3. 常用接口地址(部分)
//Access_Token 接口调用请求地址(请求方式:get) private static final String ACCESS_TOKEN_URL= "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; //上传媒体接口 private static final String UPLOAD_URL= "https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE"; //自定义菜单创建(修改)接口 ,添加菜单接口(POST) private static final String CREATE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN"; //自定义菜单查询接口 (get) private static final String QUERY_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN"; //自定义菜单删除接口(get) private static final String DELETE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN"; //消息推送相关接口(以下) //上传图文消息内的图片获取URL 接口 (post) private static final String UPLOAD_SEND_IMAGE = "https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=ACCESS_TOKEN"; //上传图文消息素材 接口 (post) private static final String UPLOAD_SEND_NEWS = "https://api.weixin.qq.com/cgi-bin/media/uploadnews?access_token=ACCESS_TOKEN"; //根据分组进行群发推送接口 (post) private static final String SEND_NEWS = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token=ACCESS_TOKEN"; //微信预览接口(post) private static final String PREVIEW_NEWS = "https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token=ACCESS_TOKEN"; //分组相关接口 //创建分组 (post) private static final String CREATE_GROUP = "https://api.weixin.qq.com/cgi-bin/groups/create?access_token=ACCESS_TOKEN"; //获取关注用户列表 private static final String GET_USER_LIST = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID"; //获取用户信息接口 (get) private static final String GET_USER_INFO = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
(下一章:微信公众号-创建菜单)