联通校园网自动登陆

南航联通校园网自动登陆

联通校园网自动登陆的方法

获取自动登陆脚本

  1. 新建一个文件夹用于存放脚本C:/AutoJobs
  2. 新建一个文件,并命名为autoAuth.pyw
  3. 将附录代码1(完成自动登陆的python代码)复制到文件
  4. 如果你未修改过密码可以不执行后两步操作(即账号为02505+密码,在下面的配置任务计划时需要额外操作)
  5. 将代码DEF_UserName设置为你的账号(字符串类型,如*“02505123456”*)
  6. 将代码DEF_PassWord设置为你的密码(字符串类型,如*“123456”*)

安装python及环境配置

  1. Python官网下载python安装包(https://www.python.org/ftp/python/3.11.9/python-3.11.9-amd64.exe)
  2. 安装python,需要勾选Add Python.exe to PATH,然后选择Install Now安装
  3. Win+R启动的运行窗口,输入cmd并回车启动命令行窗口
  4. 输入pip install beautifulsoup4并回车安装bs4库(请保证网络畅通)

配置Windows任务计划

打开计算机管理并创建任务计划

  1. Windows徽标处右键
  2. 在右键菜单中选择计算机管理
  3. 计算机管理(本地)->系统工具->任务计划程序->任务计划程序库处创建新文件夹 AutoJobs
  4. 右键新建任务
  5. 选择常规选项卡,填入名称 autoAuth描述 Nuaa联通校园网ChinaUnicom自动登陆(选填)。安全选项选择不管用户是否登陆都要运行并勾选不存储密码
  6. 选择触发器选项卡,点击新建。在打开的窗口中开始任务选择发生事件时设置选择自定义
  7. 点击新建事件筛选器,在打开的窗口中选择XML选项卡并勾选手动编辑查询,在框中填入附录代码2(自定义事件筛选器配置)
  8. 选择操作选项卡,点击新建。在打开的窗口中操作选择启动程序。在设置中,程序或脚本填入pythonw程序路径(C:/Users/[YourUserName]/AppData/Local/Programs/Python/Python311/pythonw.exe,[YourUserName]替换为你的用户名,python311替换为你的python版本),添加参数填入*-u autoAuth.pyw 密码[未配置默认密码时]*,起始于填入autoAuth脚本所在目录(C:/AutoJobs
  9. 选择条件选项卡,在电源中取消勾选只有计算机使用交流电源时才启动此任务

故障排查

错误代码(任务计划管理器的上次运行结果)

  • 0x1: 未配置用户名,即未完成1.4和1.5的操作
  • 0x2 : 未连接到目标WIFI,即触发器误触发,代码2出错
  • 0x3 : 未获取到IPv4地址,即网络故障
  • 0x4 : 获取paramStr失败,即联通的登陆方式发生变换,代码1不再适用
  • 0x5 : 认证失败,即登陆服务器返回不正常
  • 0x6 : 认证失败,凭据错误

附录

代码1

完成自动登陆的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
 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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
import subprocess
import requests
import os
import time
import sys
from bs4 import BeautifulSoup as bs
from time import sleep

# 错误代码
# 0x01 : 未配置用户名
# 0x02 : 未连接到目标WIFI
# 0x03 : 未获取到IPv4地址
# 0x04 : 获取paramStr失败
# 0x05 : 认证失败
# 0x06 : 认证失败,凭据错误

# 默认用户名和密码
# 如果环境变量中设置了ChinaUnicom_WIFI_USERNAME和ChinaUnicom_WIFI_PASSWORD,则使用这些值
# 如果提供未修改的密码,则自动生成用户名
# 如果环境变量中没有设置,则使用默认值
DEF_UserName = None
DEF_PassWord = None

if DEF_UserName is None or DEF_PassWord is None:
    USERNAME = os.getenv("ChinaUnicom_WIFI_USERNAME")
    PASSWORD = os.getenv("ChinaUnicom_WIFI_PASSWORD")
else:
    USERNAME = os.getenv("ChinaUnicom_WIFI_USERNAME", DEF_UserName)
    PASSWORD = os.getenv("ChinaUnicom_WIFI_PASSWORD", DEF_PassWord)

if USERNAME is None or PASSWORD is None:
    USERID = sys.argv[1] if len(sys.argv) > 1 else None
    if USERID is not None:
        USERNAME = "02505" + USERID
        PASSWORD = USERID

# 日志文件
LOGFILE = "auth.log"
LOG_LIMIT = 300
# 判断日志行数并删除旧日志
def clean_log():
    if os.path.exists(LOGFILE):
        with open(LOGFILE, "r", encoding="utf-8") as log_file:
            lines = log_file.readlines()
        if len(lines) > LOG_LIMIT:
            with open(LOGFILE, "w", encoding="utf-8") as log_file:
                log_file.writelines(lines[-LOG_LIMIT:])
# 日志记录函数
def logger(message):
    print(message)
    with open(LOGFILE, "a", encoding="utf-8") as log_file:
        log_file.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - {message}\n")

def end_process(message, error_code=0):
    logger(message)
    exit(error_code)

clean_log()
logger("程序启动,开始自动认证流程。")

# 检查用户名和密码是否配置
if USERNAME is None or PASSWORD is None:
    end_process("未配置用户名或密码,程序退出。", 0x01)

# 测试网络连接
CHCP_CMD = "chcp 65001"
PING_TEST_URL = "www.bing.com"
PING_TEST_CMD = f"ping -n 1 {PING_TEST_URL}"
PING_TEST_CMD = f"{CHCP_CMD} && {PING_TEST_CMD}"
def ping_test():
    try:
        result = subprocess.run(PING_TEST_CMD, shell=True, capture_output=True, text=True)
        if "TTL=" in result.stdout:
            return True
        else:
            return False
    except Exception as e:
        logger(f"网络连接测试失败: {e}")
        return False
    
# ------ 检查网络连接 ------
if ping_test():
    end_process("网络连接正常,无需认证。", 0)

# 获取连接的WIFI名称
GET_WIFI_CMD = "netsh wlan show interfaces"
GET_WIFI_CMD = f"{CHCP_CMD} && {GET_WIFI_CMD}"
ATTR_WIFI_KEYWORD = "SSID"
def get_current_wifi():
    try:
        result = subprocess.run(GET_WIFI_CMD, shell=True, capture_output=True, text=True)
        if result.returncode != 0:
            return None
        output = result.stdout
        for line in output.splitlines():
            if ATTR_WIFI_KEYWORD in line:
                return line.split(":")[1].strip()
    except Exception as e:
        logger(f"无法获取WiFi名称: {e}")
    return None

# 检查当前连接的WIFI是否为目标WIFI
DST_WIFI = "ChinaUnicom"
RETRY_TIME = 3
RETRY_COUNT = 3
def check_wifi():
    current_wifi = get_current_wifi()
    if current_wifi != DST_WIFI:
        logger(f"当前WiFi SSID: {current_wifi}. 等待连接到 {DST_WIFI}...")
        return False
    logger(f"已连接到 {DST_WIFI}.")
    return True

# ------ 等待连接到目标WIFI ------
while not check_wifi():
    sleep(RETRY_TIME)
    RETRY_COUNT -= 1
    if RETRY_COUNT <= 0:
        end_process(f"未连接到目标WIFI {DST_WIFI},请检查网络设置。", 0x02)

# 获取当前IPv4地址
GET_IP_CMD = "ipconfig"
GET_IP_CMD = f"{CHCP_CMD} && {GET_IP_CMD}"
DST_ADAPTERS = ["无线局域网适配器 WLAN", "Wireless LAN adapter WLAN"]
END_KEYWORD = "适配器"
ATTR_IPV4_KEYWORD = ["IPv4 地址", "IPv4 Address"]
RETRY_TIME2 = 3
RETRY_COUNT2 = 3
def get_wifi_ip():
    try:
        result = subprocess.run(GET_IP_CMD, shell=True, capture_output=True, text=True)
        if result.returncode != 0:
            return None
        output = result.stdout
        lines = output.splitlines()
        wlan_section = False
        
        for line in lines:
            if any(adapter in line for adapter in DST_ADAPTERS):
                wlan_section = True
                continue
            elif END_KEYWORD in line and wlan_section:
                break
            elif wlan_section and any(keyword in line for keyword in ATTR_IPV4_KEYWORD):
                ip = line.split(":")[-1].strip()
                return ip
    except Exception as e:
        logger(f"无法获取IPv4地址: {e}")
    return None

# ------ 获取当前IPv4地址 ------
CRT_IP = get_wifi_ip()
while not CRT_IP:
    logger("无法获取当前IPv4地址, 正在重试...")
    sleep(RETRY_TIME2)
    CRT_IP = get_wifi_ip()
    RETRY_COUNT2 -= 1
    if RETRY_COUNT2 <= 0:
        end_process("未获取到IPv4地址, 请检查网络设置。", 0x03)
logger(f"当前IPv4地址: {CRT_IP}")

# ------ 再次检查网络连接 ------
if ping_test():
    end_process("网络连接正常,无需认证。", 0)

# 获取连接所需的参数
BASE_URL = "http://58.240.51.118"
PAGE_URL = BASE_URL + "/?wlanuserip=" + str(CRT_IP) + "&basname=&ssid=school"
def get_paramStr():
    # 主页面
    page_info = requests.request("GET", PAGE_URL)
    if page_info.status_code != 200:
        return None
    page_context = page_info.text
    soup = bs(page_context, "html.parser")
    main_frame = soup.find("frame", {"name": "mainFrame"})
    if not main_frame:
        return None
    frame_src = main_frame["src"]   # type: ignore
    # 子页面
    frame_url = BASE_URL + str(frame_src)
    page_info2 = requests.request("GET", frame_url)
    if page_info2.status_code != 200:
        return None
    page_context2 = page_info2.text
    soup2 = bs(page_context2, "html.parser")
    paramStr_input = soup2.find("input", {"name": "paramStr"})
    paramStr = paramStr_input["value"]  # type: ignore
    return paramStr

# 自动认证
PROVINCE = "wlan.js.chinaunicom.cn"
GDYH = "prov"
URL = BASE_URL + "/authServlet"
SUCCESS_KEYWORD = "本次上网时长:"

# ------- 自动认证 --------
try:
    paramStr = get_paramStr()
    if paramStr is None:
        raise ValueError("无法从页面获取paramStr。")
except Exception as e:
    end_process(f"获取paramStr失败: {e}", 0x04)

FORM_DATA = {
    "province": PROVINCE,
    "paramStr": paramStr,
    "gdyh": GDYH,
    "shortname": "",
    "UserName": USERNAME,
    "PassWord": PASSWORD,
}

response = requests.request("POST", URL, data=FORM_DATA)
if response.status_code == 200:
    logger("收到响应,正在检查认证结果...")
    if not SUCCESS_KEYWORD in response.text:
        end_process("认证失败,请检查您的凭据。", 0x06)
else:
    end_process(f"认证失败,状态码: {response.status_code}", 0x05)

logger(f"认证成功,已完成自动认证。用户名: {USERNAME}, IPv4地址: {CRT_IP}")

# 防止连续多次触发
sleep(10)
logger("程序结束,等待下次触发。")

代码2

自定义事件筛选器配置(二选一,推荐使用ActiveHttpProbeFailedHotspotDetected触发)

连接WIFI(ChinaUnicom)时触发(不推荐)

1
2
3
4
5
6
7
8
<QueryList>
	<Query Id="0" Path="Microsoft-Windows-WLAN-AutoConfig/Operational">
	<Select Path="Microsoft-Windows-WLAN-AutoConfig/Operational">
		*[System[(EventID=11005)]] and
		*[EventData[Data[@Name='SSID']='ChinaUnicom']]
	</Select>
	</Query>
</QueryList>

检测到需要网页认证的热点(ActiveHttpProbeFailedHotspotDetected)时触发(推荐)

1
2
3
4
5
6
7
8
<QueryList>
	<Query Id="0" Path="Microsoft-Windows-NCSI/Operational">
	<Select Path="Microsoft-Windows-NCSI/Operational">
		*[System[(EventID=4042)]] and
		*[EventData[Data[@Name='CapabilityChangeReason']=7]]
	</Select>
	</Query>
</QueryList>
ActiveHttpProbeFailedHotspotDetected

HTTP 探测无法穿越热点或强制验证门户。 This is typically determined when an HTTP response 200 is received, but the response payload doesn’t contain the text file connecttest.txt. 或者,可能会收到非 200 HTTP 状态代码(如 302 或 304)。 处理无法建立无线连接的问题时,通常会观察到此状态代码。 通过数据包捕获进行验证。 用户可能需要对热点进行身份验证,或者可能需要修改热点配置。

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计