内网环境下微信扫码登录小结

您所在的位置:网站首页 微信扫码连wifi怎样收益最大呢 内网环境下微信扫码登录小结

内网环境下微信扫码登录小结

2024-01-01 06:36| 来源: 网络整理| 查看: 265

一、需求:

网站需要接入微信扫码登录,但此网站仅能在内网环境下访问,仅网站服务器可以连接微信外网

二、遇到的问题:

1、图片需要联网:

参考网页:微信网页扫码登录 按照上述网站上的指南接入,在可访问外网的情况下可以使用,但是由于二维码的图片是需要浏览器从微信的服务器中获取的,在内网情况下无法拿到图片

解决方案:可以将二维码图片爬取过来,放入登录页面的标签中 首先访问网站: https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

通过分析网页源代码可以得到二维码图片是放在一个img标签中,图片链接为一个随机uuid,因此通过正则匹配到地址后再访问该图片链接可以抓取到图片,随后可以通过服务器将图片写回到登陆页面的中: 在这里插入图片描述

后台获取图片代码:

@RequestMapping(value = "getQrCode") public void getQrCode(HttpServletResponse response, HttpServletRequest request) throws IOException { try { byte[] image = userService.getQrCode(request); //将网页上的图片转成btye数组 response.setContentType("image/jpeg"); response.getOutputStream().write(image); response.addHeader("Content-Disposition","attachment;filename=image.jpg"); }catch (Exception e) { e.printStackTrace(); } } getQrCode: 先访问https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect 获取到图片链接之后,再访问图片链接codeUrl进行访问 ResponseEntity imgEntity = restTemplate.getForEntity(codeUrl, byte[].class); 前端img标签的src填我们封装好的转接接口名称即可显示二维码

2、:获取到图片之后,扫码访问无法与前台交互

拿到了图片,发现也可以扫,手机微信也能正常识别二维码,但是点了确认登录之后,页面没有跳转到登陆页面,也没有任何反应。原因是原本微信提供了一个页面,该页面上有一些js函数,进行了一些判断(如是否扫码、取消还是成功、执行页面跳转并带上code参数进行下一步验证),由于我们只拿了图片,切断了这些步骤,自然无反应

解决方案:分析了微信网页的js,发现有一个定时函数,该函数会定期去请求一个接口,该接口的uuid参数正是之前访问二维码生成的那个uuid 如图所示:/connect/l/qrconnect?uuid=071x6Yhr0dWpH 在这里插入图片描述

随后查看该接口的返回内容: 在这里插入图片描述 可以发现该接口返回的正是微信扫码登录最关键的code和一个错误码

随后分析微信网页上的js:

!function() { function a(a) { var b = document.location.search || document.location.hash; if (b) { if (/\?/.test(b) && (b = b.split("?")[1]), null == a) return decodeURIComponent(b); for (var c = b.split("&"), d = 0; d < c.length; d++) if (c[d].substring(0, c[d].indexOf("=")) == a) return decodeURIComponent(c[d].substring(c[d].indexOf("=") + 1)) } return "" } function b(a) { jQuery.ajax({ type: "GET", url: p + "/connect/l/qrconnect?uuid=071x6Yhr0dWpHa1T" + (a ? "&last=" + a : ""), dataType: "script", cache: !1, timeout: 6e4, success: function(a, e, f) { var g = window.wx_errcode; switch (g) { case 405: var h = "http://172.17.250.142/jkpt/loadByWx"; h = h.replace(/;/g, "&"), h += (h.indexOf("?") > -1 ? "&" : "?") + "code=" + wx_code + "&state="; var i = c("self_redirect"); if (d) if ("true" !== i && "false" !== i) try { document.domain = "qq.com"; var j = window.top.location.host.toLowerCase(); j && (window.location = h) } catch (k) { window.top.location = h } else if ("true" === i) try { window.location = h } catch (k) { window.top.location = h } else window.top.location = h; else window.location = h; break; case 404: jQuery(".js_status").hide(), jQuery(".js_qr_img").hide(), jQuery(".js_wx_after_scan").show(), setTimeout(b, 100, g); break; case 403: jQuery(".js_status").hide(), jQuery(".js_qr_img").hide(), jQuery(".js_wx_after_cancel").show(), setTimeout(b, 2e3, g); break; case 402: case 500: window.location.reload(); break; case 408: setTimeout(b, 2e3) } }, error: function(a, c, d) { var e = window.wx_errcode; 408 == e ? setTimeout(b, 5e3) : setTimeout(b, 5e3, e) } }) } function c(a, b) { b || (b = window.location.href), a = a.replace(/[\[\]]/g, "\\$&"); var c = new RegExp("[?&]" + a + "(=([^&#]*)|&|#|$)") , d = c.exec(b); return d ? d[2] ? decodeURIComponent(d[2].replace(/\+/g, " ")) : "" : null } var d = window.top != window; if (!d) { document.getElementsByClassName || (document.getElementsByClassName = function(a) { for (var b = [], c = new RegExp("(^| )" + a + "( |$)"), d = document.getElementsByTagName("*"), e = 0, f = d.length; f > e; e++) c.test(d[e].className) && b.push(d[e]); return b } ); for (var e = document.getElementsByClassName("status"), f = 0, g = e.length; g > f; ++f) { var h = e[f]; h.className = h.className + " normal" } } var i = parseInt(a("styletype"), 10) , j = parseInt(a("sizetype"), 10) , k = a("bgcolor") , l = NaN; if (1 !== i && 0 !== i && 1 === l && (i = 0), 1 === i) d ? document.body.className = document.body.className + " redesign-style_iframe" + (1 === j ? " redesign-style_iframe-small" : "") : document.body.className = document.body.className + "redesign-style_page", k && (document.body.style.backgroundColor = k), jQuery(".new-template").show(); else { if (d) { var m = ""; "white" != m && (document.body.style.color = "#373737") } else document.body.style.backgroundColor = "#333333", document.body.style.padding = "50px"; if (jQuery(".old-template").show(), 0 !== i) { var n = ""; if (n) { var o = document.createElement("link"); o.rel = "stylesheet", o.href = n.replace(new RegExp("javascript:","gi"), ""), document.getElementsByTagName("head")[0].appendChild(o) } } } var p = window.usenewdomain ? "https://lp.open.weixin.qq.com" : "https://long.open.weixin.qq.com"; setTimeout(b, 100) }();

关键代码段: 在这里插入图片描述 可以发现不同状态码有不同的执行逻辑: 405时会将code加入到ridirect_url中然后进行跳转 404表示已经扫描 403表示用户扫描然后按了取消 408则是初始状态,表示无操作

因此解决方案就是将这个接口也经由网站服务器进行一层转封,每次前端轮询这个接口,查询扫码状态,每次生成二维码图片时,也将uuid存入到session当中

代码:

/** * 请求是否已经扫码 * @param response * @param request * @throws IOException */ @RequestMapping(value = "getQrCodeResult") public @ResponseBody String getQrCodeResult(HttpServletResponse response, HttpServletRequest request,String last) throws IOException { try { Object uuid=request.getSession().getAttribute("codeUUID"); if(uuid==null){ return "window.wx_errcode=408;window.wx_code='';"; } return userService.getQrCodeResult(request,last);//否则给接口发送Get请求,获取最新的状态和code信息 }catch (org.springframework.web.client.ResourceAccessException e1){ } catch(Exception e) { e.printStackTrace(); } return "window.wx_errcode=408;window.wx_code='';"; }

这里注意到有个last参数,记得要带上,表示上一次的状态码,猜测微信在这里做了处理 可以减少请求的次数。随后将上述js代码放到登录页面中,一进到页面就启动接口定时任务 进行轮询,如果用户扫码或者取消扫码,通过此接口可以更新相应的状态

3、:/connect/l/qrconnect?uuid=071x6Yhr0dWp接口缓慢,页面状态更新不及时

描述:由于我们是做了接口转发,因此状态更新有一些延时,这个需要权衡一下调用频率和轮询时间。此接口怀疑微信做了处理,当用户未进行操作时,此接口返回的状态码为408,此时从请求到结束大概需要10+秒的时间,而当用户扫了码之后,状态码变成了404,此时这个接口请求会变快,大概200ms左右。由于是通过定时器轮询,微信好像做了频率限制,因此当状态码变成404时,由于速度很快,此时我们定时轮询容易造成刷屏,此时状态码会变成666,而原生Js中没有处理此状态码的操作。此外,发现当ctrl+F5强刷网页时,定时函数好像偶尔不执行,导致扫码状态无法更新和跳转登录。

解决问题:

针对状态码变成了404,此时这个接口请求会变快造成刷屏的问题,可以加上last参数,加入之后,该请求会挂起直到用户有下一步操作,可以减少刷屏

针对666状态码 不放心可以加入一个处理分支,当遇到666时提示用户刷新二维码,然后启动新一轮计时

针对ctrl+F5强刷网页时,定时函数好像偶尔不执行,导致扫码状态无法更新的问题 目前我自己的解决方案是后台访问该接口时,加入超时时间,当超过一定时间直接返回给前台,因为怀疑就是该接口一开始需要十几秒的访问时间造成的

三、其他:

1如果用了spring security进行管理的话,上述接口都不要屏蔽,否则就无法获取结果了(被拦截了) 2、绕过spring security,后台登录:

UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails((HttpServletRequest) request)); SecurityContextHolder.getContext().setAuthentication(authentication); request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,SecurityContextHolder.getContext());

3、本文仅用于自己记录学习用,如需要转载,请注明出处。如有错漏,欢迎指正。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3