需求
网站实现扫描二维码关注微信公众号,如果已经关注公众号就自动登陆网站并获取其微信昵称,头像等信息,如果用户未关注就等用户关注公众号后自动登陆网站
--如果用户已关注公众号,网站端直接自动登陆,如果没有关注,就等用户关注公众号之后网站端自动登陆
(目前已经完成了这个功能,示例网址: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(); } } }); }
--本文意在对网站实现微信公众号用户扫码关注登录的实现思路做了概述
--这个方案中轮训访问是个问题,也是可以改进的地方,但是目前没有更好的方案,如果您有什么更好的建议,欢迎留言给点思路