一个校园内上网验证脚本的实现

缘起

在我们学校里,使用学校的网络想要访问外网,就必须要进行登录验证。每个学生或者老师都有自己的上网账号,那就是学号/工号以及自己设置的密码啦。
一般来说,进行验证的时候,都会在弹出一个网页来让我们输入用户名而密码。但是,有些服务器可没有浏览器,因为它们根本就没有图形界面,只有字符界面和命令行,那就会出现一个问题,它们就没法通过浏览器打开网页的方式来提交用户名和密码了。

实验室的师兄想出来使用一个 Python 脚本来模拟登录验证。这个脚本一直使用的好好的,直到前段时间,学校升级了后台验证接口,原先的脚本就不能用了。

所以我就想着,那我重新写一个脚本吧,于是说干就干。

网页分析

首先进行网页分析,利用 chrome 的 web 调试工具,可以看到点击“登录”按钮,实际上执行的是 onPwdLogin 这个 js 函数。

于是我在 Sources 中的 js 文件中寻找到该函数

可以看到它调用了 loginRequest 函数,并且传递了 4 个参数:

1
2
3
4
5
6
var params = {
opr:'pwdLogin',//smsLogin表示短信认证登录,pwdLogin表示密码认证登录
userName : $id("password_name").value,
pwd : $id("password_pwd").value,
rememberPwd : $id("rememberPwd").checked ? '1':'0'
};

继续寻找loginRequest 函数

省略中间部分代码

可以看到,它最后调用的是 $ajax 这个函数,并且传递了之前的参数

在这个函数中,我们终于看到了实质上请求发送代码,它发送了 POST 请求给 g_url 这个变量代表的地址,并且传递了那几个参数,g_url 这个全局变量在最上面有定义。

看到这里,我终于知道了登录验证的整个过程。于是我用 postman 模拟了一下 POST 请求。
一开始我将参数放在 POST 请求的 body 中,并且将请求的 Content-Type 设置为 application/json ,然后发现不行,后来又去看了一下,发现服务器接受的请求的Content-Type 是为 application/x-www-form-urlencoded
并且,经过测试,发现 opruserNamepwd 这三个参数不能缺少,否则会返回 404,但是 rememberPwd 是可以省略的,大概默认就是不记住密码。

最后看到了返回结果,我知道了请求基本上基本上就是这样子了,于是我就准备开始写 Python 脚本了。

Python 脚本

最后的 Python脚本,我就直接贴代码了,脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Auther: Mercy
# E-mail: gyd0915@gmail.com
# Date: 2017-02-28
#
# Connect to the Internet without login UI.
#

import urllib
import urllib2
import getpass
import json

# Login URL
login_url = 'http://192.168.2.253/ac_portal/login.php'


def login():
# input username & password
username = raw_input('Enter your username:')
password = getpass.getpass('Enter your password:')

# form data. 'opr': 'pwdLogin' means login by password, it is a necessary parameter.
data = {'opr': 'pwdLogin', 'userName': username, 'pwd': password}
form_data = urllib.urlencode(data)

# post the request
req = urllib2.Request(login_url, form_data)
res = urllib2.urlopen(req)

# get the result string and decode it as dictionary
res_str = res.read().replace("'", '"')
res_dic = json.loads(res_str)

# show the login result
if res_dic.get('success') is True:
print "Login successful!"
else:
print "Login failed!\nReason: %s" % res_dic.get('msg').encode('utf-8')


if __name__ == '__main__':
login()

脚本写起来是非常简单的,不算注释的话,也就短短二三十行代码。

主要使用到了 urllib2 这个自带库来发送 POST 请求。密码输入的时候用到了 getpass 这个库,这样就看不到输入密码了。

总共就五个步骤,1、先输入用户名和密码;2、然后将其拼接成 POST 请求的表单数据;3、接着发送 POST 请求;4、获取返回结果,将其转化为字典格式;5、最后根据请求返回结果,输出登陆成功或者失败,以及其他信息。

最后运行了一下,又能愉快地在服务器上联网了。