1. 项目背景与需求解析
校园网认证是每个大学生都绕不开的日常操作。在山东科技大学,GiWiFi作为主流无线网络认证系统,学生们每天都需要反复进行登录操作。特别是在实验室、图书馆等需要频繁切换设备的场景,手动输入账号密码的过程显得尤为繁琐。
我注意到身边同学普遍存在三个痛点:首先是每次断网重连都要重新认证,其次是多人共用账号时需要反复登录不同设备,最后是夜间断网后无法自动重连影响文件传输等后台任务。这些看似小的不便,在四年大学生活中累积起来会浪费大量时间。
基于Python的自动化脚本恰好能完美解决这些问题。通过模拟浏览器登录行为,我们可以实现断网自动重连、多设备同时在线、定时保活等功能。这个项目不仅具有实用价值,更能帮助初学者理解网络协议和自动化编程的核心思想。
2. 技术方案设计
2.1 登录流程逆向分析
GiWiFi的认证流程典型分为四个阶段:探测网络连通性、获取认证页面、提交凭证信息、验证登录状态。通过Chrome开发者工具抓包,可以发现几个关键接口:
- 连通性检测接口:访问任意HTTP网站会被302重定向到
http://172.16.1.1:8080 - 认证页面接口:
/portal/api/v1/get_agent返回认证页面所需参数 - 登录提交接口:
/portal/api/v1/login接收加密后的账号密码 - 心跳保持接口:
/portal/api/v1/heartbeat每5分钟需要请求一次
特别需要注意的是,密码字段使用了RSA加密,公钥通过/portal/api/v1/get_public_key接口动态获取。这要求脚本必须具备实时获取和解析公钥的能力。
2.2 核心模块设计
脚本采用分层架构设计,主要包含以下模块:
python复制class GiWiFiAutoLogin:
def __init__(self, username, password):
self.session = requests.Session()
self.auth_url = "http://172.16.1.1:8080"
self.credentials = {
'username': username,
'password': password
}
def check_network(self):
# 检测网络连通性
pass
def get_public_key(self):
# 获取RSA公钥
pass
def encrypt_password(self):
# RSA加密密码
pass
def submit_login(self):
# 提交登录请求
pass
def keep_alive(self):
# 维持心跳连接
pass
3. 关键实现细节
3.1 RSA加密处理
GiWiFi采用的动态RSA公钥机制是最大技术难点。通过分析网页JavaScript代码,发现其使用JSEncrypt库进行加密。我们需要用Python实现相同逻辑:
python复制from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
def rsa_encrypt(public_key, message):
key = RSA.importKey(public_key)
cipher = PKCS1_v1_5.new(key)
ciphertext = cipher.encrypt(message.encode())
return base64.b64encode(ciphertext).decode()
实际测试发现,直接从接口获取的公钥需要添加头尾标记才能被正确解析:
python复制def format_public_key(raw_key):
return f"-----BEGIN PUBLIC KEY-----\n{raw_key}\n-----END PUBLIC KEY-----"
3.2 会话保持机制
登录成功后,服务器会返回一个sessionid作为身份凭证。我们需要处理以下细节:
- Cookie自动管理:使用
requests.Session()自动处理cookie - 心跳间隔控制:每300秒发送一次心跳请求
- 异常重试机制:当心跳失败时自动重新登录
python复制def keep_alive(self):
while True:
try:
resp = self.session.get(f"{self.auth_url}/portal/api/v1/heartbeat")
if resp.json().get('result') != 1:
self.submit_login()
except Exception as e:
print(f"心跳异常: {str(e)}")
time.sleep(300)
4. 完整实现代码
以下是整合后的完整脚本:
python复制import requests
import time
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
class GiWiFiAutoLogin:
def __init__(self, username, password):
self.session = requests.Session()
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
})
self.auth_url = "http://172.16.1.1:8080"
self.credentials = {
'username': username,
'password': password
}
def check_network(self):
try:
resp = requests.get("http://connect.rom.miui.com", timeout=5)
return False if resp.status_code == 302 else True
except:
return False
def get_public_key(self):
url = f"{self.auth_url}/portal/api/v1/get_public_key"
resp = self.session.get(url)
return self.format_public_key(resp.json().get('data'))
@staticmethod
def format_public_key(raw_key):
return f"-----BEGIN PUBLIC KEY-----\n{raw_key}\n-----END PUBLIC KEY-----"
def rsa_encrypt(self, public_key, message):
key = RSA.importKey(public_key)
cipher = PKCS1_v1_5.new(key)
ciphertext = cipher.encrypt(message.encode())
return base64.b64encode(ciphertext).decode()
def submit_login(self):
public_key = self.get_public_key()
encrypted_pwd = self.rsa_encrypt(public_key, self.credentials['password'])
payload = {
'username': self.credentials['username'],
'password': encrypted_pwd,
'ifautologin': '1',
'pagesign': 'secondauth',
'usripadd': ''
}
login_url = f"{self.auth_url}/portal/api/v1/login"
resp = self.session.post(login_url, data=payload)
return resp.json().get('result') == 1
def keep_alive(self, interval=300):
while True:
try:
if not self.check_network():
self.submit_login()
resp = self.session.get(f"{self.auth_url}/portal/api/v1/heartbeat")
if resp.json().get('result') != 1:
self.submit_login()
except Exception as e:
print(f"[Error] {time.ctime()}: {str(e)}")
time.sleep(interval)
if __name__ == "__main__":
login = GiWiFiAutoLogin("你的学号", "你的密码")
if login.submit_login():
print("登录成功,开始保持连接...")
login.keep_alive()
else:
print("登录失败,请检查账号密码")
5. 部署与使用指南
5.1 环境准备
需要安装以下Python库:
bash复制pip install requests pycryptodome
5.2 运行方式
- 将脚本保存为
giwifi_auto_login.py - 修改文件末尾的学号和密码
- 直接运行
python giwifi_auto_login.py - 建议使用nohup或tmux保持后台运行:
bash复制nohup python giwifi_auto_login.py > login.log 2>&1 &
5.3 开机自启动
对于Windows用户:
- 创建批处理文件
start_giwifi.bat
bat复制@echo off
python D:\path\to\giwifi_auto_login.py
- 将快捷方式放入启动文件夹
shell:startup
对于Linux用户:
- 创建systemd服务文件
/etc/systemd/system/giwifi.service
ini复制[Unit]
Description=GiWiFi Auto Login
After=network.target
[Service]
ExecStart=/usr/bin/python3 /path/to/giwifi_auto_login.py
Restart=always
User=root
[Install]
WantedBy=multi-user.target
- 启用服务:
bash复制systemctl enable giwifi
systemctl start giwifi
6. 常见问题排查
6.1 登录返回错误码
| 错误码 | 含义 | 解决方案 |
|---|---|---|
| -1 | 系统错误 | 等待一段时间后重试 |
| -2 | 参数错误 | 检查账号密码格式 |
| -3 | 密码错误 | 确认密码是否正确 |
| -4 | 账号不存在 | 检查学号是否正确 |
| -5 | 账号停用 | 联系网络中心 |
6.2 其他异常情况
- 连接超时:检查是否已连接到GiWiFi无线网络
- 加密失败:确认pycryptodome库已正确安装
- 心跳中断:可能是服务器主动断开,脚本会自动重连
- 多设备冲突:GiWiFi允许3个设备同时在线,超出会导致踢线
7. 安全注意事项
- 账号密码建议使用配置文件存储,不要硬编码在脚本中
- 脚本所在目录权限应设置为仅当前用户可读
- 公共电脑上使用后记得删除包含密码的文件
- 不建议修改心跳间隔小于5分钟,可能触发风控
- 定期检查脚本是否正常运行,避免欠费导致服务中断
这个项目让我深刻体会到校园网认证背后的技术细节,特别是RSA加密在Web安全中的应用。通过抓包分析,还发现了几个有趣的API接口,比如可以查询在线设备和流量使用情况,有兴趣的同学可以继续扩展这些功能。