自动连接校园网wifi脚本实践(自动网页认证)

抽了一下空将自己编写的一个自动化校园网连接脚本的编写思路整理了一下。该脚本只测试过fosu的校园网,但其他校园网的连接方式应该也类似。

起因

经常从实验室、教室和宿舍来回穿梭,每次都要手动连接校园网,非常麻烦。因此,我决定编写一个脚本,自动连接校园网。

思路

首先要对校园网的连接方式进行分析,fosu的校园网是通过网页认证的方式连接的,因此,我选择了使用python的requests库来模拟网页认证。

分析校园网登录逻辑

该过程中需要使用到浏览器的开发者工具,博主使用的是edge浏览器,因此使用了edge的F12功能(快捷键Ctrl+Shift+I)来进行分析。但博主先前给开发者工具设置了中文,因此在下面的图片中出现的文字都是中文,会在博文中用()来标注对应的英文。

登录校园网认证网页,先点击F12,选中网络(Network),并勾选保存日志(Preserve log)。

示例图片

再在网页输入账户密码,点击login,看看发了哪些请求(一般network的前几个比较重要,后面的都是资源文件)

示例图片

这里发现发了两个请求,第一个post请求,第二个get请求(每个学校的不一样,fosu的可以直接用本教程方法)
第一个请求post,标头(Headers)中有:

示例图片

查看一下负载(Payload),查看post请求携带的数据,发现里面包含了自己的校园网账户和密码

示例图片

第二个请求get:

示例图片

复制上面的request URL到浏览器,发现就是可以访问的认证界面。

总结:点击login按钮后,我的密码是先通过一个post请求提交到服务器,然后再通过一个get请求去实现登录的。

那脚本要做的事情就是:当发现当前处于未登录状态时,要模拟浏览器的行为,先发一个post请求,请求header和data都要和浏览器上的内容对应,然后再发一个get请求就可以了。(有点像爬虫做的事情)

判断当前是否处于未登录状态

一般提交get请求时,当前已经登录和为登录状态得到html文档中的标签内容是不一样的。这里我们在已登录状态下,按F12,再按ctrl+F查找下的内容,发现果然有标识。

示例图片

也就是说,当get请求结果返回的title标签内容是“登录成功”时,当前就已经处于登录状态了,此时不需要再进行认证,如果不是tittle标签内容不是“登录成功”,则说明当前未登录,需要发请求进行认证。

代码设计

首先梳理一下脚本的大致功能逻辑,然后后面再对代码进行功能扩充、优化修改。

粗设计

    1. 写一个死循环,不断判断当前是否处于登录状态:直接发get请求,如果返回的tittle为“登录成功”,则为已登录状态。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      while(True):
      print("自动联网脚本开始运行...")
      try:
      # 请求校园网url
      response = request.urlopen(get_URL)
      html = response.read()
      # 获取title元素内容
      res = re.findall('<title>(.*)</title>', html.decode(encoding="GBK", errors="strict"))
      print('res:', res)
      title = ''
      if len(res) == 0:
      print("访问",get_URL,"失败,请检查请求地址!")
      pass
      else:
      title = res[0]
      print("title:",title)
    1. 否则的话,就模拟浏览器的行为,给服务器发一个post请求(设置好header和data,示例如下),然后再发一个get请求进行认证。
  • 2.1 设置header

示例图片

将请求标头(Request Headers)中的内容复制到代码中,注意header中的内容是字典类型的,因此要将其转换为字典类型。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
header = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
"Connection": "keep-alive",
"Content-Length": "781",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Cookie": "EPORTAL_COOKIE_DOMAIN=false; EPORTAL_COOKIE_SAVEPASSWORD=true; EPORTAL_COOKIE_OPERATORPWD=; EPORTAL_COOKIE_NEWV=true; EPORTAL_COOKIE_PASSWORD=0c8e03e937910c800890ec544b109fce824381097eba5b4e91011f2c3615a959563377abf814d21320f9f0c98a6615cf42b17d3920473acdb51447afc135c59e2e8a69b4136afde0b623a64e56fc0a82f330f70ce253235e999cc858bff65867a22053c8186fd148a20e0ba3d45ced58808990a864a4f39c72f368ddf0086582; EPORTAL_AUTO_LAND=; EPORTAL_COOKIE_SERVER=%E7%A7%BB%E5%8A%A8; EPORTAL_COOKIE_SERVER_NAME=%E7%A7%BB%E5%8A%A8; EPORTAL_USER_GROUP=Student; EPORTAL_COOKIE_USERNAME=; JSESSIONID=BD9937825302ECA25A22692E95D92CF1",
"Host": "10.10.9.4",
"Origin": "http://10.10.9.4",
"Referer": "http://10.10.9.4/eportal/index.jsp?wlanuserip=10.201.220.90&wlanacname=FSN-XX-Business&ssid=&nasip=10.10.9.1&snmpagentip=&mac=44fa66c84741&t=wireless-v2-plain&url=http://www.msftconnecttest.com/redirect&apmac=&nasid=FSN-XX-Business&vid=3326&port=472&nasportid=AggregatePort%20175.33260000:3326-0",
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 Edg/131.0.0.0",
}
  • 2.2 设置data

示例图片

将请求负载(Payload)中的内容复制到代码中,注意data中的内容是字典类型的,因此要将其转换为字典类型。示例如下:

1
2
3
4
5
6
7
8
9
10
data = {
"userId": 'XXXX', # 需要根据自己的情况修改
"password": '0c937910c800890ec544b109fce824381097eba5b4e91011f2c3615a9595abf814d21320f9f0c98a642b17d3920473acdb51447afc135c59e2e9b413e0b623a64e56fc0a82f330f70ce253235e999cc85a22053c8186fd148a20e0ba3d45ced58808990a864a4f39c72f3f0086582', # 需要根据自己的情况修改
"queryString": 'wlanuserip%3D10.201.220.90%26wlanacname%3DFSN-XX-Business%26ssid%3D%26nasip%3D10.10.9.1%26snmpagentip%3D%26mac%3D44fa66c84741%26t%3Dwireless-v2-plain%26url%3Dhttp%3A%2F%2Fwww.msftconnecttest.com%2Fredirect%26apmac%3D%26nasid%3DFSN-XX-Business%26vid%3D3326%26port%3D472%26nasportid%3DAggregatePort%2520175.33260000%3A3326-0',
"passwordEncrypt": 'true',
"operatorPwd": '',
"operatorUserId": '',
"validcode": '',
"service": '%E7%A7%BB%E5%8A%A8',
}
    1. 发post请求和get请求,并打印状态码(状态码为200则为成功)
1
2
3
4
5
6
7
8
9
10
# 发送post请求(设置好header和data)
post_response = requests.post(post_URL, data, headers=header)
print(f"post请求状态码 {post_response.status_code}") # 修正状态码获取方式
uft_str = post_response.text.encode("iso-8859-1").decode('utf-8')

# 发送get请求(修改响应变量名)
schoolWebLoginURL = get_URL
get_status = requests.get(schoolWebLoginURL).status_code
print(f"get请求状态码 {get_status}")

    1. 休眠一段时间,然后进行下一次循环。
1
2
3
4
# 每1h左右检测一次是否成功连接
rand = random.uniform(0, 100)
print("休眠",int(3600.0 + rand),"s")
time.sleep(3600.0 + rand)

这样我们就有了一个初始功能的脚本代码了

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import re    # 正则表达式,用于匹配字符
from urllib import request
import requests
import time
import random
import os

# 设置日志文件路径
log_file_path = 'D:/win11/desktop/auto_connect.log'

# 第一个post请求的URL
post_URL = 'http://10.10.9.4/eportal/InterFace.do?method=login'
# 第二个get请求的URL(浏览器可访问的url)
get_URL = 'http://10.10.9.4/eportal/success.jsp?userIndex=30313761383634643038313231376666613631393530663863623836663665345f31302e3230312e3232302e39305f3230323430333930313432&keepaliveInterval=0'

while(True):
print("自动联网脚本开始运行...")
try:
# 请求校园网url
response = request.urlopen(get_URL)
html = response.read()
# 获取title元素内容
res = re.findall('<title>(.*)</title>', html.decode(encoding="GBK", errors="strict"))
print('res:', res)
title = ''
if len(res) == 0:
print("访问",get_URL,"失败,请检查请求地址!")
pass
else:
title = res[0]
print("title:",title)
# 根据title元素内容判断是否处于已登录状态
if title == '登录成功':
print('当前状态为:已登陆成功!')
else:
print('当前状态为:未登录!')
# 设置post的请求头,浏览器点击F12,在Netword中选中post请求,点击Headers、request header面板中查看
header = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
"Connection": "keep-alive",
"Content-Length": "781",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Cookie": "EPORTAL_COOKIE_DOMAIN=false; EPORTAL_COOKIE_SAVEPASSWORD=true; EPORTAL_COOKIE_OPERATORPWD=; EPORTAL_COOKIE_NEWV=true; EPORTAL_COOKIE_PASSWORD=0c8e03e937910c800890ec544b109fce824381097eba5b4e91011f2c3615a959563377abf814d21320f9f0c98a6615cf42b17d3920473acdb51447afc135c59e2e8a69b4136afde0b623a64e56fc0a82f330f70ce253235e999cc858bff65867a22053c8186fd148a20e0ba3d45ced58808990a864a4f39c72f368ddf0086582; EPORTAL_AUTO_LAND=; EPORTAL_COOKIE_SERVER=%E7%A7%BB%E5%8A%A8; EPORTAL_COOKIE_SERVER_NAME=%E7%A7%BB%E5%8A%A8; EPORTAL_USER_GROUP=Student; EPORTAL_COOKIE_USERNAME=; JSESSIONID=BD9937825302ECA25A22692E95D92CF1",
"Host": "10.10.9.4",
"Origin": "http://10.10.9.4",
"Referer": "http://10.10.9.4/eportal/index.jsp?wlanuserip=10.201.220.90&wlanacname=FSN-XX-Business&ssid=&nasip=10.10.9.1&snmpagentip=&mac=44fa66c84741&t=wireless-v2-plain&url=http://www.msftconnecttest.com/redirect&apmac=&nasid=FSN-XX-Business&vid=3326&port=472&nasportid=AggregatePort%20175.33260000:3326-0",
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 Edg/131.0.0.0",
}
# 设置post的请求数据,浏览器点击F12,在Netword中选中post请求,点击payload面板中查看
data = {
"userId": 'XXXX', # 需要根据自己的情况修改
"password": '0c937910c800890ec544b109fce824381097eba5b4e91011f2c3615a9595abf814d21320f9f0c98a642b17d3920473acdb51447afc135c59e2e9b413e0b623a64e56fc0a82f330f70ce253235e999cc85a22053c8186fd148a20e0ba3d45ced58808990a864a4f39c72f3f0086582', # 需要根据自己的情况修改
"queryString": 'wlanuserip%3D10.201.220.90%26wlanacname%3DFSN-XX-Business%26ssid%3D%26nasip%3D10.10.9.1%26snmpagentip%3D%26mac%3D44fa66c84741%26t%3Dwireless-v2-plain%26url%3Dhttp%3A%2F%2Fwww.msftconnecttest.com%2Fredirect%26apmac%3D%26nasid%3DFSN-XX-Business%26vid%3D3326%26port%3D472%26nasportid%3DAggregatePort%2520175.33260000%3A3326-0',
"passwordEncrypt": 'true',
"operatorPwd": '',
"operatorUserId": '',
"validcode": '',
"service": '%E7%A7%BB%E5%8A%A8',
}
# 发送post请求(设置好header和data)
post_response = requests.post(post_URL, data, headers=header)
print(f"post请求状态码 {post_response.status_code}") # 修正状态码获取方式
uft_str = post_response.text.encode("iso-8859-1").decode('utf-8')

# 发送get请求(修改响应变量名)
schoolWebLoginURL = get_URL
get_status = requests.get(schoolWebLoginURL).status_code
print(f"get请求状态码 {get_status}")

except Exception as e:
print(f"发生错误: {str(e)}")
error_msg = str(e)

with open(log_file_path, 'a', encoding='utf-8') as log_file:
log_file.write(f"[{time.ctime()}] 自动联网脚本开始运行\n")
log_file.write(f"当前状态: {'已登陆成功' if title == '登录成功' else '未登录'}\n")
if 'post_response' in locals():
log_file.write(f"POST状态码: {post_response.status_code}\n")
if 'get_status' in locals():
log_file.write(f"GET状态码: {get_status}\n")
if 'error_msg' in locals():
log_file.write(f"错误信息: {error_msg}\n")
log_file.write("-"*50 + "\n")

# 检查文件大小,如果大于1KB则清空文件
if os.path.getsize(log_file_path) > 1024:
open(log_file_path, 'w').close()

# 每1h左右检测一次是否成功连接
rand = random.uniform(0, 100)
print("休眠",int(3600.0 + rand),"s")
time.sleep(3600.0 + rand)

优化

    1. 检测设备的网络连接状态。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      def check_network_connection():
      """检查网络连接状态"""
      targets = [
      "http://connectivitycheck.gstatic.com/generate_204",
      "http://www.msftconnecttest.com/connecttest.txt",
      "http://connect.rom.miui.com/generate_204"
      ]
      timeout = 8

      for url in targets:
      try:
      with urllib.request.urlopen(url, timeout=timeout) as response:
      if response.status == 204:
      return True
      except:
      continue
      return False
    1. 根据网络连接状态来决定是否进行自动化连接。
      1
      2
      3
      4
      while True:
      while not check_network_connection():
      auto_login(post_URL, get_URL, log_file_path)
      print("网络已恢复,自动联网脚本结束运行")
    1. 再将之前的休眠改动位置,更改时间为1min左右。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      while True:
      while not check_network_connection():
      auto_login(post_URL, get_URL, log_file_path)
      print("网络已恢复,自动联网脚本结束运行")

      # 每1min左右检测一次是否成功连接
      rand = random.uniform(0, 20)
      print("休眠",int(60.0 + rand),"s")
      time.sleep(60.0 + rand)

最终代码

注:有关参数的设置需要按照上面的步骤结合自己实际的情况进行修改,还不支持直接下载源码使用!!!

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import re    # 正则表达式,用于匹配字符
from urllib import request
import urllib.request
import requests
import time
import random
import os

def check_network_connection():
"""检查网络连接状态"""
targets = [
"http://connectivitycheck.gstatic.com/generate_204",
"http://www.msftconnecttest.com/connecttest.txt",
"http://connect.rom.miui.com/generate_204"
]
timeout = 8

for url in targets:
try:
with urllib.request.urlopen(url, timeout=timeout) as response:
if response.status == 204:
return True
except:
continue
return False
def auto_login(post_URL, get_URL, log_file_path):
print("自动联网脚本开始运行...")
try:
# 请求校园网url
response = request.urlopen(get_URL)
html = response.read()
# 获取title元素内容
res = re.findall('<title>(.*)</title>', html.decode(encoding="GBK", errors="strict"))
print('res:', res)
title = ''
if len(res) == 0:
print("访问",get_URL,"失败,请检查请求地址!")
pass
else:
title = res[0]
print("title:",title)
# 根据title元素内容判断是否处于已登录状态
if title == '登录成功':
print('当前状态为:已登陆成功!')
else:
print('当前状态为:未登录!')
# 设置post的请求头,浏览器点击F12,在Netword中选中post请求,点击Headers、request header面板中查看
header = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
"Connection": "keep-alive",
"Content-Length": "781",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Cookie": "EPORTAL_COOKIE_DOMAIN=false; EPORTAL_COOKIE_SAVEPASSWORD=true; EPORTAL_COOKIE_OPERATORPWD=; EPORTAL_COOKIE_NEWV=true; EPORTAL_COOKIE_PASSWORD=0c8e03e937910c800890ec544b109fce824381097eba5b4e91011f2c3615a959563377abf814d21320f9f0c98a6615cf42b17d3920473acdb51447afc135c59e2e8a69b4136afde0b623a64e56fc0a82f330f70ce253235e999cc858bff65867a22053c8186fd148a20e0ba3d45ced58808990a864a4f39c72f368ddf0086582; EPORTAL_AUTO_LAND=; EPORTAL_COOKIE_SERVER=%E7%A7%BB%E5%8A%A8; EPORTAL_COOKIE_SERVER_NAME=%E7%A7%BB%E5%8A%A8; EPORTAL_USER_GROUP=Student; EPORTAL_COOKIE_USERNAME=; JSESSIONID=BD9937825302ECA25A22692E95D92CF1",
"Host": "10.10.9.4",
"Origin": "http://10.10.9.4",
"Referer": "http://10.10.9.4/eportal/index.jsp?wlanuserip=10.201.220.90&wlanacname=FSN-XX-Business&ssid=&nasip=10.10.9.1&snmpagentip=&mac=44fa66c84741&t=wireless-v2-plain&url=http://www.msftconnecttest.com/redirect&apmac=&nasid=FSN-XX-Business&vid=3326&port=472&nasportid=AggregatePort%20175.33260000:3326-0",
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 Edg/131.0.0.0",
}
# 设置post的请求数据,浏览器点击F12,在Netword中选中post请求,点击payload面板中查看
data = {
"userId": 'XXXX', # 需要根据自己的情况修改
"password": '0c937910c800890ec544b109fce824381097eba5b4e91011f2c3615a9595abf814d21320f9f0c98a642b17d3920473acdb51447afc135c59e2e9b413e0b623a64e56fc0a82f330f70ce253235e999cc85a22053c8186fd148a20e0ba3d45ced58808990a864a4f39c72f3f0086582', # 需要根据自己的情况修改
"queryString": 'wlanuserip%3D10.201.220.90%26wlanacname%3DFSN-XX-Business%26ssid%3D%26nasip%3D10.10.9.1%26snmpagentip%3D%26mac%3D44fa66c84741%26t%3Dwireless-v2-plain%26url%3Dhttp%3A%2F%2Fwww.msftconnecttest.com%2Fredirect%26apmac%3D%26nasid%3DFSN-XX-Business%26vid%3D3326%26port%3D472%26nasportid%3DAggregatePort%2520175.33260000%3A3326-0',
"passwordEncrypt": 'true',
"operatorPwd": '',
"operatorUserId": '',
"validcode": '',
"service": '%E7%A7%BB%E5%8A%A8',
}
# 发送post请求(设置好header和data)
post_response = requests.post(post_URL, data, headers=header)
print(f"post请求状态码 {post_response.status_code}") # 修正状态码获取方式
uft_str = post_response.text.encode("iso-8859-1").decode('utf-8')

# 发送get请求(修改响应变量名)
schoolWebLoginURL = get_URL
get_status = requests.get(schoolWebLoginURL).status_code
print(f"get请求状态码 {get_status}")

except Exception as e:
print(f"发生错误: {str(e)}")
error_msg = str(e)

with open(log_file_path, 'a', encoding='utf-8') as log_file:
log_file.write(f"[{time.ctime()}] 自动联网脚本开始运行\n")
log_file.write(f"当前状态: {'已登陆成功' if title == '登录成功' else '未登录'}\n")
if 'post_response' in locals():
log_file.write(f"POST状态码: {post_response.status_code}\n")
if 'get_status' in locals():
log_file.write(f"GET状态码: {get_status}\n")
if 'error_msg' in locals():
log_file.write(f"错误信息: {error_msg}\n")
log_file.write("-"*50 + "\n")

# 检查文件大小,如果大于1KB则清空文件
if os.path.getsize(log_file_path) > 1024:
open(log_file_path, 'w').close()


if __name__ == "__main__":
log_file_path = 'auto_connect.log'
# 确保日志文件存在
if not os.path.exists(log_file_path):
open(log_file_path, 'w').close()

post_URL = 'http://10.10.9.4/eportal/InterFace.do?method=login'
get_URL = 'http://10.10.9.4/eportal/success.jsp?userIndex=30313761383634643038313231376666613631393530663863623836663665345f31302e3230312e3232302e39305f3230323430333930313432&keepaliveInterval=0'

while True:
while not check_network_connection():
auto_login(post_URL, get_URL, log_file_path)
print("网络已恢复,自动联网脚本结束运行")

# 每1min左右检测一次是否成功连接
rand = random.uniform(0, 20)
print("休眠",int(60.0 + rand),"s")
time.sleep(60.0 + rand)

注意:本脚本只是用于自动做校园网账户的认证(在已经连着校园网的情况下才能验证),并不是让windows去自动连接某个校园网wifi。所以在平时使用时建议把校园网wifi的“自动连接”打开。或者尝试自己设计自动连接校园网wifi的脚本。(博主尝试过,但自动连接wifi时总会在桌面弹出校园网的浏览器登录网页,博主嫌功能一般、累赘就没添加)

脚本需要安装依赖的模块和库,出现警告时检查是否缺少依赖模块。

开机自启动

提供两种方式,一种是将编写好的Python源码编译成可执行文件exe,另一种是使用bat批处理文件来执行Python脚本(这种便于开发调试,实时修改)。

编译成可执行文件exe

  1. 安装pyinstaller模块

    1
    pip install pyinstaller
  2. 编译成可执行文件

    1
    pyinstaller -F AutoConnect.py

    编译好的exe文件在dist目录下

  3. win+r,输入shell:startup

  4. 把.exe拖入开机自启动文件夹里面

bat批处理脚本

  1. 编写bat批处理文件
    新建一个bat文件,内容如下:
    1
    2
    3
    @echo off
    python "这里填写你的Python脚本路径"
    pause
  2. win+r,输入shell:startup
  3. 把.bat拖入开机自启动文件夹里面

最终成品效果

示例图片

脚本已推送到github:Auto_Connect