0基础开发 zzuwlan 自动认证脚本的总结

应该是巨坑,超长且啰嗦

为什么要有这个东西

先来看看认证界面是什么样的:
认证界面
看起来多么简单粗暴,完美秉承了 simple is the best 的原则(口胡不下去了
反正就是每次都要打个验证码还要点点点真 TM 烦,作为一个程序员怎么能每天干这么机械的活!
说搞就搞,先去买本犀牛书看….(卒

开发思路

首先通过 chrome 的网络分析工具,我们得知首先你请求任意一个地址,zzuwlan 都会将你劫持到认证的页面去,代码大概是长这样的:
认证劫持
然后就是上图那样的认证界面,输入用户名密码和验证码点击认证就 OK 了,看起来是不是非常简单?
确实也不难,但是为了防止你用脚本完成这个过程(我猜的),他们设置了一些坑。
首先确定脚本的思路,我们需要使用tampermonkey来运行自定义脚本,这个东西是干嘛的我就不啰嗦了。
脚本一开始的思路是:随便 get 一个 http 的网址(因为 https 的网址会卡在证书验证上),然后拿到认证地址,访问,填充用户名和密码,验证码我们选择直接手撸 ocr 验证,然后后面提交就是了。
当然,既然是一开始。。。就证明我踩坑了。

开发中踩的那些坑

首先,我们来看这个认证页面:
zzuwlan 第一页
嗯,原来是内嵌了个 iframe 啊,也就是说认证的主体其实是 http://202.196.64.132:8080/login0.htm
然后看看当前页的网址栏,有没有发现什么不同?
没错,这个坑爹学校居然玩跨域。。。于是纯浏览器端的 js 瞬间被打死(跨域限制),不过我想出了一个非常 trick 的方法。。。

window.location.href="http://202.196.64.132:8080/";

山不过来,我便过去。
解决了这个问题,我们终于来到了认证页面,这时候我们非常随意的用 document.querySelector 来给账号和密码赋值,然后是验证码。
我校总算是没有上传说中的文字验证码(笑),这个验证码虽然看起来不难,而且现在网上遍地都是免费的 OCR 接口,但是我是在写 WiFi 的自动认证啊!哪有网络可用啊摔
没有网络也代表了一件事:你不能用任何第三方的 js 库,除非你愿意把那一坨塞进一个几 KB 大的脚本里。
不过这么简单的东西也不是特别需要第三方的库,作为新手还是多写点原生比较好(
首先我们来分析一下这个验证码:
zzuwlan 验证码
长宽不变,字符间距也不变,有随机的噪点,但是字符没有扭曲和变形,且没有长条色带干扰。
那么我们的处理思路就是,首先灰度二值,然后我们看到此时的图片基本是黑白两色了。由于字符间距不变,我们可以直接切割图片来分析明暗值。
将明暗值转换为01存进字符串,我们就手动实现了一个验证码识别库(雾)
然而在调用 OCR 函数的时候遇到了一个问题,这个图片它本身也是跨域的。。。 所以我们不能在一般认证的首页来直接拿到它,于是最后还是用了 js 跳转来处理跨域。
处理好验证码后我们的表单就填充完了,本来是想写一个 ajax 来提交的,然后一看源码,又是跨域。
zzuwlan post
其实油猴本身提供了一个 GM_xmlhttpRequest 方法来让你跨域提交,但是这个东西在第一次使用的时候会弹窗要求你允许此次跨域请求。。。考虑到普通用户的体验,没有用这种办法而是用传统的模拟点击了。(其实我很好奇为什么直接点击就能跨域 post?)
认证提交后返回了一个页面,里面有个函数 zzjwlan() ,里面的方法就是跳转到一个加了时间戳和 seed 的页面去。这样认证流程就结束了。
回头看这第一版的脚本,简单粗暴的 IIFE ,毫无设计模式,各种硬编码,作为一个有点理想的程序员怎么能忍这个。
开始重构。

重构不是长久之计

由于第一版的设计里直接硬编码了账号和密码,在这一版本我决定使用 cookies 来存储。
第一次访问的时候脚本检测 cookies,如果没有的话就弹窗输入并保存起来,之后就可以多次使用。
还有第一版并没有考虑弱网条件下的各种问题,比如验证码可能还没加载好,于是在一番摸索和请教后使用监听器完成了验证码的加载。
说到这个验证码的加载,学校的设计挺有意思,页面渲染的时候首先用一张”正在载入”的图片占位,然后他写了一个函数动态替换了那个图片,所以我一开始直接选择元素只能取到”正在载入”那张图片。之后在 饼少
美羽的帮助和教导下使用监听器完成了这个功能。
由于测试的时候多次重现了各种各样的错误,于是这个版本也加入了错误的处理,代码写的很直接,还是选择 dom 元素然后 substring 来分析是什么错误。我觉得这样写大概是不太科学的,但是并没有别的思路,如果你有更好的方法还请不吝赐教。
重构的第二版本大概就做了这些事,还有一点就是把一些函数给独立出来,放在 IIFE 里顺序执行了,这个是 java 里的写法,果然没基础硬写总会感觉坑坑的。。。还是要多看看别人的优秀代码啊。

反思与总结

三天时间写了这么个小玩具,其实个人还是不太满意的。因为即使重构过一遍,代码还是很乱,而且自己对 js 和网页的执行顺序等基础知识掌握还十分不牢固,果然纸上得来终觉浅,还是应该多学习。
另外有个插曲,在开发的时候因为是面向谷歌编程(雾),有几次因为某墙干扰太严重用了百度,然后就踩了一个坑。。。
在处理验证码的输入的时候,百度出来的结果基本都是让你来用 onchange 啊 oninput 这样的方法,我就照着填了上去。。。然后测试就被坑了一个小时,最后跑去看了 MDN 和 爆栈 的讲解后总结了这么几点:
1.onchange事件在内容改变(两次内容有可能还是相等的)且失去焦点时触发.
2.onpropertychange事件是实时触发,js 操作的改变也可以触发,但是这个事件是 IE only。。。
3.oninput事件在value改变时实时触发,但是不响应 js 改变和浏览器下拉框中保存的选项 。。。
于是这个地方我还是用 settimeout 来处理了,非常不优雅,如果你有什么解决方法还请不吝赐教。
总结:百度谁用谁傻逼

最后用一句乔布斯的话来结束吧。

Stay hungry, stay foolish.

项目地址求 star 及各种代码 review