Swift模拟强智教务系统登录

需求

因为学校的教务系统版本比较老旧,XX领导也不重视这个,现在的系统只能勉强运行,兼容性问题很多,随着Win10系统IE11的更新,随之而来的问题将会越来越多。
一直想做一款适合在学校范围内使用,但功能又不局限于查课表(区别于超级课程表)的一款App,毕竟作为系统管理员,可以直接对数据库进行操作,可实现的功能也很多。

思路

做App的第一步就是要如何进行登陆验证,教务系统的数据库上密码是密文保存的,教务系统提供找回密码的功能可以将密码(明文)发送至绑定邮箱,但暂时不知道密码的加密方式及密钥,对密码直接进行解密无从下手,另外一种发放是通过其他方式完成验证,学院的微信平台就是这样,在进行身份绑定时,利用用户提供的姓名、身份证号、学号等个人信息进行身份绑定。

新途径

起初我也是按照上面的思路进行构想的,但App不同于微信,总要有登录名和密码进行登陆,让学生先注册账号再进行身份绑定显得有些多余(这个想法我也已经写好代码,并且功能基本实现了=。=),最近突发奇想,超级课程表是怎么完成验证,从而获得学生课表的,意思顺着这个思路,找到了新的登陆验证方法,模拟教务系统登录进行身份验证,这种方法去除了注册账号的麻烦,对学生来说在学校一个账号就最够了,多了显然很麻烦,也不是好事儿。

功能实现

借助第三方Alamofire的强大功能,来进行HTTP请求,模拟教务系统的登陆。Alamofire的具体配置请参照Github上的介绍,网上也有很多。
首先使用Chrome登陆教务系统,通过开发者工具我们可以看到在登录时向服务器发送了哪些请求,如下图:

在发送用户名和密码的同时,也向服务器发送了另外3个参数,在打开登陆页面时,这三个参数就已经隐藏在网页中了,第一步我们要做的就是获取这3个参数及值,通过Alamofire请求网页,然后对HTML代码进行解析,获取我们需要的内容。

1
2
3
4
5
6
7
8
9
let jwURL = "http://kwjw.jsnu.edu.cn/jwgl/Login.aspx"
Alamofire.request(self.jwURL).responseString{ response in
let html = response.result.value //获取网页内容html文本
if let doc = try? HTML(html: html!, encoding: .utf8) { //将Html文本抓换成Html代码进行解析
for link in doc.css("input[type='hidden']") { //通过Html元素来获取值,类似于JQuery的方法
self.parameters[link["id"]!] = link["value"]! //将获取的key和value保存到数组中,后面使用
}
}
}

获取到这个后,用户名和密码可以通过用户输入,而最麻烦的是验证码,在网上查了好多方法,有的提供的解决办法是,在请求过程中,将进程暂停几秒中,让用户将验证码保存到指定的文件内,来通过验证,另外一种方法就是通过图片识别验证码,完成自动输入。两种方法都可行,但是在测试过程中,我发现我们的系统不需要验证验证码,就可以成功登陆了,不知道其他的教务系统是否可以。
知道这个重大利好后,后面的工作就容易多了,再次通过Alamofire请求登陆页面,这次发送数据给服务器,

1
2
3
4
5
6
7
8
Alamofire.request(jwURL, method: .post, parameters: parameters).responseString{ response in
//parameters为上一步获取的数据,及用户输入的用户名和密码
let html = response.result.value
if let doc = try? HTML(html: html! as String, encoding: .utf8) {
print(doc.title!) //通过获取登陆后的一些网页内容,来确认学生是否登陆成功
//do something...
}
}

总结

通过以上方法即可模拟强智教务系统的登陆,继而可以继续开发其他功能。
不知其他版本的系统是否需要完善验证码这一步骤,待验证。

Some Bugs

开发过程中发现发现有个学生的账号登陆就闪退,调试后发现通过Alamofire获取的内容为nil,而报错,仔细分析后发现,学生名字中含有生僻字,也许是因为编码方式的原因,Alamofire使用UTF8编码,而教务系统使用GB2312编码方式,从而造成报错,因此修改代码如下:

1
2
3
Alamofire.request(self.jwURL, method: .post, parameters: parameters).response{ response in //修改请求方式
let html = NSString(data: response.data!, encoding: CFStringConvertEncodingToNSStringEncoding(UInt32(CFStringEncodings.GB_18030_2000.rawValue))) //将请求结果通过GB方式编码
if ... //后续步骤

重新调试运行发现问题解决。