需求
網站實現掃描二維碼關注微信公眾號,如果已經關注公眾號就自動登陸網站並獲取其微信昵稱,頭像等信息,如果用戶未關注就等用戶關注公眾號后自動登陸網站
--如果用戶已關注公眾號,網站端直接自動登陸,如果沒有關注,就等用戶關注公眾號之后網站端自動登陸
(目前已經完成了這個功能,示例網址:http://www.bid-data.com/ 愛招標——免費實時標訊推送平台,為企業負責人及商務人員即時掌控招標動態、中標檢索、競爭對手中標項目分析等服務平台。)
做微信掃碼登陸,生成二維碼必須是微信公眾號中綁定的域這個域名,網站生成不了二維碼(網站與微信服務器不是同一個域名) ,而是調用微信系統的接口獲取二維碼,用戶掃碼后也是請求微信服務器
實現思路
1,微信的系統,提供生成帶參數的二維碼的接口,這個參數就是唯一值(場景值)
2,網站調用微信系統,獲取生成的二維碼圖片
3,用戶掃碼會直接調用微信服務器,將用戶訪問微信服務器的信息記錄到redis,key就是唯一值(場景值)
4,網站端做輪訓去查詢redis中是否有這個唯一值的數據,如果有就獲取用戶信息登錄,沒有就五秒一次輪訓,登錄后就不在做輪訓(從二維碼彈出之后開始做輪訓,關閉二維碼后停止輪訓)
2,網站調用微信系統,獲取生成的二維碼圖片
3,用戶掃碼會直接調用微信服務器,將用戶訪問微信服務器的信息記錄到redis,key就是唯一值(場景值)
4,網站端做輪訓去查詢redis中是否有這個唯一值的數據,如果有就獲取用戶信息登錄,沒有就五秒一次輪訓,登錄后就不在做輪訓(從二維碼彈出之后開始做輪訓,關閉二維碼后停止輪訓)
5,這里的唯一值是可以自己定義的,我用的是截取了幾位的時間戳
實現步驟
(1)微信端:寫一個獲取帶參數的臨時二維碼接口。
// 臨時二維碼 private final static String QR_SCENE = "QR_SCENE"; // 永久二維碼 private final static String QR_LIMIT_SCENE = "QR_LIMIT_SCENE"; // 永久二維碼(字符串) private final static String QR_LIMIT_STR_SCENE = "QR_LIMIT_STR_SCENE"; // 創建二維碼 private String create_ticket_path = "https://api.weixin.qq.com/cgi-bin/qrcode/create"; // 通過ticket換取二維碼 private String showqrcode_path = "https://mp.weixin.qq.com/cgi-bin/showqrcode"; @RequestMapping("getQrcode") public @ResponseBody String getQrcode(@RequestParam(value = "sceneId")int sceneId) throws Exception{ String ticket = createTempTicket(tokenService.getToken(),"2592000",sceneId); LOGGER.info("get wechat qrcode ==> start"); LOGGER.info("sceneId :"+sceneId); LOGGER.info("ticket :"+ticket); LOGGER.info("get wechat qrcode ==> end"); return ticket; } /** * 創建臨時帶參數二維碼 * @param accessToken * @expireSeconds 該二維碼有效時間,以秒為單位。 最大不超過2592000(即30天),此字段如果不填,則默認有效期為30秒。 * @param sceneId 場景Id * @return */ public String createTempTicket(String accessToken, String expireSeconds, int sceneId) { TreeMap<String,String> params = new TreeMap<String,String>(); params.put("access_token", accessToken); Map<String,Integer> intMap = new HashMap<String,Integer>(); intMap.put("scene_id",sceneId); Map<String,Map<String,Integer>> mapMap = new HashMap<String,Map<String,Integer>>(); mapMap.put("scene", intMap); Map<String,Object> paramsMap = new HashMap<String,Object>(); paramsMap.put("expire_seconds", expireSeconds); paramsMap.put("action_name", QR_SCENE); paramsMap.put("action_info", mapMap); String data = new Gson().toJson(paramsMap); String tse = HttpRequestUtil.HttpsDefaultExecute(HttpRequestUtil.POST_METHOD,create_ticket_path,params,data); JSONObject jsonObject = JSONObject.fromObject(tse); LOGGER.info("ticket :"+jsonObject.getString("ticket"));
return showqrcode_path+"?ticket="+jsonObject.getString("ticket");
}
accessToken就是調用微信接口的憑證token
(2)網站端:網站寫一個調用微信生成二維碼的接口
@RequestMapping("getQrcode") public @ResponseBody Hashtable getQrcode(int sceneId){ System.out.println(sceneId); Hashtable param = new Hashtable(); param.put("sceneId", sceneId); String qrcodePath = HttpUtil.postRequest(Constant.getValue("get_qrcode"), param); System.out.println(" qrcodePath ==> "+qrcodePath); param.put("path", qrcodePath); return param; }
直接使用http調用接口就行,Constant.getValue("get_qrcode")這個就是微信提供二維碼接口的url
(3)微信端:微信處理用戶請求(這個地址是微信公眾號填的那個地址,微信服務器會將所有用戶請求轉發到這個地址)
// 事件推送 else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) { // 事件類型 String eventType = requestMap.get("Event"); // 關注 if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) { //獲取用戶信息 String requestUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID"; requestUrl = requestUrl.replace("ACCESS_TOKEN", tokenService.getToken()) .replace("OPENID", fromUserName); JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, "GET",null); String nickname = jsonObject.getString("nickname"); String address = jsonObject.getString("country")+"-"+jsonObject.getString("province")+"-"+jsonObject.getString("city"); String headimgurl = jsonObject.getString("headimgurl");
//將用戶信息存入redis,key為唯一值(場景值) Hashtable params = new Hashtable(); params.put("phoneIme", fromUserName); params.put("state", 1); params.put("location", address); params.put("realName", nickname); params.put("nickname", nickname); params.put("headimgurl", headimgurl); if(StringUtils.isNotBlank(eventKey)){ redisCacheTool.setDataToRedis(eventKey.replace("qrscene_", ""), 3600, params); System.out.println("qrcode redis key ==> "+eventKey.replace("qrscene_", "")); params.put("equipmentType", eventKey); }
//入庫 HttpUtil.postRequest(Constant.getValue("UPDATE_USER"), params); } // 取消關注 else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) { // TODO 取消訂閱后用戶不會再收到公眾賬號發送的消息,因此不需要回復 Hashtable params = new Hashtable(); params.put("phoneIme", fromUserName); params.put("state", 0); HttpUtil.postRequest(Constant.getValue("UPDATE_USER_ANS"), params); } // 掃描帶參數二維碼 else if (eventType.toLowerCase().equals(MessageUtil.EVENT_TYPE_SCAN)) { // TODO 處理掃描帶參數二維碼事件 if(StringUtils.isNotBlank(eventKey)){ String requestUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID"; //獲取用戶信息 requestUrl = requestUrl.replace("ACCESS_TOKEN", tokenService.getToken()) .replace("OPENID", fromUserName); JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, "GET",null); String nickname = jsonObject.getString("nickname"); String address = jsonObject.getString("country")+"-"+jsonObject.getString("province")+"-"+jsonObject.getString("city"); String headimgurl = jsonObject.getString("headimgurl");
//將用戶信息存入redis,key為唯一值(場景值) Hashtable params = new Hashtable(); params.put("nickname", nickname); params.put("headimgurl", headimgurl); params.put("location", address); redisCacheTool.setDataToRedis(eventKey, 3600, params); System.out.println("qrcode redis key ==> "+eventKey); } respContent = "返回的信息"; textMessage.setContent(respContent); respXml = MessageUtil.messageToXml(textMessage); }
(4)網站端:登陸頁面中做輪訓,每隔幾秒查詢一次redis,如果有用戶信息就登陸
var timestamp = new Date().getTime() + ""; var str = timestamp.substring(8, timestamp.length); window.setInterval(function() { getUser(cont); }, 10000); function getUser() { $.ajax({ type : 'get', data : { sceneId : str }, dataType : 'json', url : "getUser.do", success : function(data) { if (data.msg == "success") { location.reload(); } }, error : function(data) { if (data.msg == "success") { location.reload(); } } }); }
--本文意在對網站實現微信公眾號用戶掃碼關注登錄的實現思路做了概述
--這個方案中輪訓訪問是個問題,也是可以改進的地方,但是目前沒有更好的方案,如果您有什么更好的建議,歡迎留言給點思路