一、需求:

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

二、遇到的问题:

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<byte[]> imgEntity = restTemplate.getForEntity(codeUrl, byte[].class);
前端img标签的src填我们封装好的转接接口名称即可显示二维码

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

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

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

随后查看该接口的返回内容:

可以发现该接口返回的正是微信扫码登录最关键的code和一个错误码

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

 <script>!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(/&amp;/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}elsewindow.top.location = h;elsewindow.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")} elsedocument.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)}();</script>

关键代码段:

可以发现不同状态码有不同的执行逻辑:
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、本文仅用于自己记录学习用,如需要转载,请注明出处。如有错漏,欢迎指正。

内网环境下微信扫码登录小结相关推荐

  1. 内嵌式js微信扫码登录及自定义样式

    关于微信扫码登录网站的功能介绍,请阅读官方文档[网站应用微信登录开发指南] 根据官方文档我们知道微信扫码登录有两种方式 一种是跳转到一个大黑屏二维码界面进行扫码登录: (参见博客:PHP实现跳转式微信 ...

  2. 【网课平台】Day10.对接第三方:实现微信扫码登录

    文章目录 一.需求:微信扫码登录 1.接口文档 2.开发环境准备 3.接入分析 4.接口定义 5.申请令牌 6.查询用户信息 7.保存用户信息 一.需求:微信扫码登录 (和第三方对接的流程) 1.接口 ...

  3. 集成企业微信,企业微信扫码登录和企业微信容器内免密登录

    项目上为了支持新的业务,扩展了通过企业微信扫码登录和通过企业微信容器内的直接访问应用服务的免密登录. 1.扫码登录 企业微信以Userid作为企业内的用户身份唯一标识,集成接口可以参考官网文档: ht ...

  4. 微信官方你真的懂OAuth2?Spring Security OAuth2整合企业微信扫码登录

    ❝ 企业微信扫码登录DEMO参见文末. 现在很多企业都接入了企业微信,作为私域社群工具,企业微信开放了很多API,可以打通很多自有的应用.既然是应用,那肯定需要做登录.正好企业微信提供了企业微信扫码授 ...

  5. 微信扫码登录只能填一个授权回调域问题

    背景 公司增加了个微信扫码登录,费劲千辛万苦终于把应用申请下来了,但遇到了一个头疼的事情:微信授权回调域只能写一个,且不支持通配.这下可好了,总不能把每个需要微信登录的二级域名都申请一个应用吧?而且一 ...

  6. 第三方登录之微信扫码登录

    文章目录 1. 申请微信接入: 2. 项目环境搭建: 3.后端Controller接口: 4.HTML页面代码: 5.测试结果: 6.补充说明: 小伙伴们有各种疑问可以去参考官方文档进行详细的学习下 ...

  7. 项目整合微信扫码登录功能

    项目整合微信登录功能 一.准备工作 https://open.weixin.qq.com 1.注册 2.邮箱激活 3.完善开发者资料 4.开发者资质认证 准备营业执照,1-2个工作日审批.300元 5 ...

  8. 通过微信扫码登录剖析 oauth2 认证授权技术

    本文目录 前言 趣味解读oauth2 oauth2精髓 oauth2核心概念 结合微信登录深刻理解oauht2 本文小结 前言 相信很多小伙伴在学习 JAVA 的过程中或多或少接触或者开发过类似于 x ...

  9. 基于Springboot2.x+vue3.x整合实现微信扫码登录

    第1章 准备工作 1.1 微信开放平台 微信扫码登录,需要在微信开放平台注册账号被认证为开发者才能接入官网地址:https://open.weixin.qq.com/ 1.1.1 注册账号并认证成为开 ...

最新文章

  1. 使用ES6的Promise完美解决回调地狱
  2. 20万数据 sql 快还是 java快?_H2数据库学习(一)
  3. python3 类实例化流程
  4. 数学知识在游戏中的运用
  5. 摄像头YUV2格式详解
  6. 开源的 DNS 转发软件 Dnsmasq 被曝7个漏洞,可劫持数百万台设备
  7. Java 序列化和反序列化
  8. linux在路径下创建文件,从可以在Linux中打开的文件路径创建文件
  9. 苹果Mac突然没有声音,3 种方法快速检测
  10. 台式计算机有线无线网卡设置,笔记本/台式电脑有线网络转无线wifi教程
  11. OSChina 周一乱弹 —— 年迈渔夫遭黑帮袭抢
  12. 使用snap安装microk8s
  13. grunt html模块化管理插件,grunt模块化配置
  14. QT的自动滚动区QScrollArea的用法,图文详解
  15. matlab系统频域分析,基于MATLAB的系统频域分析的实现
  16. linux进程挂掉 自动启动,配置systemd以在Linux崩溃后自动启动服务
  17. kotlin一点摸索
  18. Jenkins流水线配置
  19. 随机数字信号处理期末大报告——基于卡尔曼滤波的自由落体运动目标跟踪MATLAB实现
  20. Be a part of making the world’s first people powered tablet

热门文章

  1. Vue引用原生高德地图标注
  2. 周涨粉超30w B站UP主非非宇Fay粉丝增长密码是什么?
  3. 风向值与风向描述定义
  4. 有MDF文件和LDF文件之后怎么创建数据库
  5. 创新案例分享 | 医院DRG系统建设项目,助力精细化分析医疗数据
  6. 不惑之年一次性通过软考高项的苦与乐
  7. HTTP状态码(查询专用)
  8. http状态码查询表(转载)
  9. Git简单生成生成公钥和私钥方法
  10. 产品设计公司的头脑风暴是什么?