需求:登录用户产生一个二维码,下一个人扫描这个二维码之后关注公众号,登录之后自动成为第一个用户的粉丝
吐槽一把,这个需求很操蛋。。。。但是,操不操蛋那是你程序猿说的不?微信开发本来就相当操蛋了。。。都懂得,文档又少,限制又多,傻逼的很。。。。
首先看下微信官方的文档,我们先了解下需要哪些东西
当然毫不犹豫的点开。。。 然后发现这个。。
却发现,并没有神马卵用 。。。。。。。。 生成二维码需要token ,我们就按照他提供的方法来搞一搞了。。。 就按照他提供的方法搞一搞了。。
二维码的几种生成方式,在这里就不解释了。。
不够简单粗暴,我直接上java代码了。。。。。
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.URL; import net.sf.json.JSONObject; public class Test { static String wechatAppID = ""; static String wechatAppSecret = ""; static String wechatAccessTokenUrl = ""; public static String getWechatAccessToken() { try { URL object = new URL(wechatAccessTokenUrl + "&appid=" + wechatAppID + "&secret=" + wechatAppSecret); HttpURLConnection con = (HttpURLConnection) object.openConnection(); con.setDoOutput(true); con.setDoInput(true); con.setRequestProperty("Content-Type", "application/json"); con.setRequestProperty("Accept", "application/json"); con.setRequestMethod("GET"); OutputStreamWriter wr = new OutputStreamWriter(con.getOutputStream(), "utf-8"); wr.flush(); // 显示 POST 请求返回的内容 StringBuilder sb = new StringBuilder(); int HttpResult = con.getResponseCode(); if (HttpResult == HttpURLConnection.HTTP_OK) { BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "utf-8")); String line = null; while ((line = br.readLine()) != null) { sb.append(line + "\n"); } br.close(); System.out.println("" + sb.toString()); } else { System.out.println(con.getResponseMessage()); } return sb.toString(); } catch (Exception e) { e.printStackTrace(); } return ""; } public static String getUserQRCode(Integer userId) { JSONObject jsonObject = JSONObject.fromObject(getWechatAccessToken()); String access_token = jsonObject.getString("access_token"); try { URL object = new URL("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + access_token); HttpURLConnection con = (HttpURLConnection) object.openConnection(); con.setDoOutput(true); con.setDoInput(true); con.setRequestProperty("Content-Type", "application/json"); con.setRequestProperty("Accept", "application/json"); con.setRequestMethod("POST"); JSONObject data = new JSONObject(); data.put("action_name", "QR_LIMIT_SCENE"); JSONObject dojb = new JSONObject(); dojb.put("scene_id", userId); JSONObject cojb = new JSONObject(); cojb.put("scene", dojb); data.put("action_info", cojb); OutputStreamWriter wr = new OutputStreamWriter(con.getOutputStream(), "utf-8"); wr.write(data.toString()); wr.flush(); // 显示 POST 请求返回的内容 StringBuilder sb = new StringBuilder(); int HttpResult = con.getResponseCode(); if (HttpResult == HttpURLConnection.HTTP_OK) { BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "utf-8")); String line = null; while ((line = br.readLine()) != null) { sb.append(line + "\n"); } br.close(); JSONObject jsonObj = JSONObject.fromObject(sb.toString()); String ticket = jsonObj.getString("ticket"); 生成二维码 return "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + ticket; } else { System.out.println(con.getResponseMessage()); } } catch (Exception e) { e.printStackTrace(); } return ""; } }
运行这两段代码之后,我们发现我们的二维码居然生成了 ,第二个方法返回一个字符串就是二维码图片的绝对路径。。。。 生成的二维码我在这里就不解释了 。。 截出来也没有神马卵用。。。 我们回头看看方法。。。
我们发现我们用的是scene_id ,而没有使用字符串,为啥子???????? 这个问题很关键二维码是生成了,但是怎么把二维码里面的id和下一个用户关联起来了?下一个用户关注上一个用户的二维码,我擦,我怎么知道啥时候关注了了 ???? 如果把上下级用户关联起来了 ? 重重问题 。。。 各种蛋疼。。。
首先,当用户点击微信公众号关注的时候,后台是如何知道哪个用户点击了关注了 ? 那么就有这么一个问题,那就是通信的问题,当用户点击关注,微信公众号如何跟我们自己的后台通信了 ???? 这个很关键
你需要在微信公众号的后台做一些配置,打开后台
IP白名单,就是哪些IP有权限调用这些接口,一般配置你服务器的IP和你开发电脑的公网IP(不是192.168.0.111),好吧,来张图吧。。
然后启动这东西 。。。
然后填写以下配置。。。
URL:其实就是一个外网可以访问的接口,注意:必须是80或443端口,做过后台都懂得。。。 这个接口的作用是,被微信调用,然后产生回调。。。。。也就是微信通过这个接口你的服务器发送消息。这个很重要
Token:自己定义的一个字符串,加密的要是,你写jinsanpang都可以;可以参考我的另外一篇文章了解token(http://blog.csdn.net/chmod_r_755/article/details/75554735)
EncodeKey:随机生成,干啥子用的,不知道,问微信
模式:明文模式,你想怎样? 加密? 搞得跟真的似的 ,有意思吗?金融机构自己看着办 。。。。
到这里,我们就明白了,用户关注微信我们的后台是怎么知道的。。。原来是有通信的 。。。这块我们就必须搞一个接口了,让微信回调。。。。。。
说太抽象了 。。。。 我们首先来张图 。。
当我扫码关注的时候, 微信会回调这个接口,注意我这边日志是json格式的,但是网上有xml版本的
这种格式的,因为我把xml转json了,xml看着都蛋疼 ,来段代码压压惊
@RequestMapping(value = "/wx.do") public void get(@RequestBody(required = false) String body, HttpServletRequest request, HttpServletResponse response) throws Exception { org.json.JSONObject jsonObject = JSONML.toJSONObject(body); logger.info("请求进来了..." + jsonObject); org.json.JSONArray jsonArray = jsonObject.getJSONArray("childNodes"); org.json.JSONObject fromUser = null; org.json.JSONObject event = null; org.json.JSONObject eventKey = null; for (int i = 0; i < jsonArray.length(); i++) { org.json.JSONObject obj = jsonArray.getJSONObject(i); String tagName = obj.getString("tagName"); if ("FromUserName".equals(tagName)) { fromUser = obj; } else if ("Event".equals(tagName)) { event = obj; } else if ("EventKey".equals(tagName)) { eventKey = obj; } } logger.info("请求进来了...fromUser=" + fromUser.toString() + " event=" + event.toString() + " eventKey=" + eventKey.toString()); if (event != null && eventKey != null && fromUser != null) { org.json.JSONArray eventArray = event.getJSONArray("childNodes"); org.json.JSONArray eventKeyArray = eventKey.getJSONArray("childNodes"); org.json.JSONArray fromUserNameArray = fromUser.getJSONArray("childNodes"); logger.info("请求进来了...eventArray=" + eventArray.toString() + " eventKeyArray=" + eventKeyArray.toString() + " fromUserNameArray=" + fromUserNameArray.toString()); if (eventArray.length() > 0 && fromUserNameArray.length() > 0 && eventKeyArray.length() > 0) { String subscribeString = eventArray.getString(0); // 动作 String fromUserString = fromUserNameArray.getString(0); // openId String eventKeyString = eventKeyArray.getString(0); // 带入的信息 logger.info("请求进来了...subscribeString=" + subscribeString + " fromUserString=" + fromUserString + " eventKeyString=" + eventKeyString); if ("subscribe".equals(subscribeString)) { // 点击关注, 保存上级的id 且保存openId // 点击取消关注,删除这条记录 // 如果 eventKeyString 为空字符串 ,表示关注官方二维码关注的 /// 包含上级Id ,则保存到数据库中 if (eventKeyString.contains("qrscene_")) { String userIdString = eventKeyString.replaceAll("qrscene_", ""); Integer userId = Integer.parseInt(userIdString); WechatKey wechatKey = new WechatKey(); wechatKey.setOpenId(fromUserString); wechatKey.setUserId(userId); wechatKeyManager.saveWechatkey(wechatKey); } } else if ("unsubscribe".equals(subscribeString)) { WechatKey wechatKey = wechatKeyManager.getWechatKeyByOpenId(fromUserString); if (wechatKey != null) { wechatKeyManager.deleteByOpenId(fromUserString); } } } } Enumeration pNames = request.getParameterNames(); while (pNames.hasMoreElements()) { String name = (String) pNames.nextElement(); String value = request.getParameter(name); // out.print(name + "=" + value); String log = "name =" + name + " value =" + value; logger.error(log); } String signature = request.getParameter("signature");/// 微信加密签名 String timestamp = request.getParameter("timestamp");/// 时间戳 String nonce = request.getParameter("nonce"); /// 随机数 String echostr = request.getParameter("echostr"); // 随机字符串 PrintWriter out = response.getWriter(); if (CheckUtil.checkSignature(signature, timestamp, nonce)) { out.print(echostr); } out.close(); out = null; }
关注的时候微信会给服务器推送一条消息
subscribe:表示开始关注
fromUser:表示哪个用户关注的,这里返回的是当前用户的openId ,唯一的
EventKey:qrscene_20 前面的qrscene_是固定的,后面的20 也就是上个用户的id
那么到了这一步, 上个用户和下一个用户就联系在一起了。。。。。。如果扫的码是系统生成的。。。EventKey:就为空字符串了。。
扯到这里了 。。。。 其他的不解释,自己理解。。。。