我之前写过用py的一些库模拟浏览器联网的方式,但是过于臃肿,这次我舍弃了py,转向了ps1脚本的发包来面向win系统,具体实现方法如下:
我们学校登录网站的过程是典型的校园网Web 认证(Portal Authentication)过程,客户端向认证服务器发送一个包含用户凭证和设备信息的 HTTP GET 请求,服务器验证后返回认证结果.
1.请求分析
目标服务器: http://学校认证服务器:6060/quickauth.do
请求方法: GET
2. 查询字符串参数详解 (Query String Parameters)
userid: 用户身份标识
格式:[用户名]@[域名后缀(运营商)]
passwd: 用户密码
wlanuserip: 客户端 IP 地址
wlanacname: 无线接入控制器 (WLAN AC) 名称(固定)
wlanacIp: 无线接入控制器 (WLAN AC) IP 地址(固定)
mac: 客户端物理地址 (MAC Address)
ssid: 服务集标识符 (Service Set Identifier)
vlan: 虚拟局域网 ID (Virtual Local Area Network ID)(设备固定)
portalpageid: 门户页面 ID
portaltype: 门户类型
version: 协议或接口版本
timestamp: 时间戳
uuid: 通用唯一识别码 (Universally Unique Identifier)。
hostname: 客户端主机名
bindCtrlId: 绑定控制 ID
由于请求代码是固定的,在发送请求时只需要固定的格式即可,为了能够实现程序的可复用性,多设备使用性,我选择
1.保存用户的数据为一个txt文件
2.自动获取到wlanuserip,mac,vlan,hostname等等不固定的数据:
我一开始是使用系统自带的查询来获取,由于我的电脑上有虚拟机(虚拟ip跟学校网络的汇聚层还有点像,导致我在这个方面花费了不少功夫),其中vlan的实在是无法直接查询得到了,后来我发现,如果通过直接访问网站重定向的 portal 页面的URL内容,来获取wlanuserip,mac,vlan,hostname等等不固定的数据更加容易且方便,但是使用这一方案一直实现不了,故放弃这个方案,改为让用户复制url保存。
程序实现:
1. 核心通信原理
脚本的核心是模拟校园网 Web 认证的交互逻辑:放弃浏览器渲染页面的冗余流程,直接通过 PowerShell 原生的Invoke-WebRequest cmdlet 构造并发送符合认证协议的 HTTP GET 请求,跳过前端页面跳转、Cookie 校验等非必要环节。
2. 配置管理
所有固定请求参数(如认证接口地址BaseURL、接入点标识WlanAcName、认证类型PortalType等)统一封装在结构化配置中(重构版为NetworkConfig类,原始版为$config哈希表),避免硬编码散落在代码中;
单独指定加密凭证文件路径($credentialsFilePath),将用户敏感信息(账号、密码、VLAN ID)与业务逻辑解耦,便于配置迁移和版本迭代。
3. 安全化凭证管理
加载逻辑:脚本启动时优先调用Get-StoredCredentials函数校验凭证文件的存在性、JSON 格式完整性和字段有效性;若凭证有效,通过 Windows DPAPI(非 Base64 编码)解密密码并填充至配置;
初始化逻辑:若首次运行或凭证失效(如格式错误、解密失败),则引导用户完成信息录入:选择运营商并自动拼接账号后缀、校验 VLAN ID 格式有效性、以隐藏方式读取密码;
持久化逻辑:用户确认保存后,通过ConvertTo-SecureString将明文密码转换为加密的安全字符串(底层依赖 DPAPI 绑定当前用户 / 机器),结合账号、VLAN ID 存入隐藏文件,并通过文件属性和 NTFS 权限控制保障本地存储安全。
4. 动态参数自动采集
针对随网络环境变化的参数,脚本实现 “无人工干预的自动采集”:
基于Get-NetAdapter、Get-NetIPAddress等网络 cmdlet,通过自定义函数Get-WifiIpAddress/Get-WirelessMacAddress筛选活跃无线网卡,动态获取当前 Wi-Fi 的 IPv4 地址和标准化格式的 MAC 地址(统一转换为冒号分隔的小写形式);
电脑主机名直接读取系统环境变量$env:COMPUTERNAME,确保参数与当前设备绑定。
5. 标准化请求构建
为避免请求解析异常,脚本对请求参数做严格的标准化处理:
生成唯一uuid和毫秒级时间戳(timestamp),满足认证服务器的唯一性校验要求;
对用户名、密码、主机名等可能含特殊字符的参数,通过[Uri]::EscapeDataString()做 URL 编码,防止参数截断、解析错误;
按校园网认证协议要求,将所有静态配置、动态采集、随机生成的参数拼接为合法的 GET 请求查询字符串(Query String)。
6. 高容错的请求发送与结果解析
脚本对认证请求全链路做异常捕获和语义化结果处理:
调用Invoke-WebRequest发起 GET 请求,设置超时时间并启用基础解析模式(-UseBasicParsing),适配无 IE 环境的 Windows 系统;
捕获网络异常(如超时、DNS 失败、SSL 错误)和服务器响应异常(如 4xx/5xx 状态码),解析响应体并输出结构化错误信息;
基于服务器返回的关键字(如"code":"0"代表成功、"code":"1"代表账号不存在、"code":"44"代表非法接入)做语义化判断,向用户输出精准的认证结果提示(成功 / 账号错误 / 接入异常等)
代码部分:
(我使用的终端:PowerShell 7,运行不了去评论区下载PowerShell/PowerShell):
if ($PSVersionTable.PSVersion.Major -lt 5 -or ($PSVersionTable.PSVersion.Major -eq 5 -and $PSVersionTable.PSVersion.Minor -lt 1)) {
Write-Host "错误:本脚本需要PowerShell 5.1或更高版本。" -ForegroundColor Red
Read-Host "按Enter退出"
exit 1
}
class NetworkConfig {
[string] $BaseURL
[string] $WlanAcName
[string] $WlanAcIp
[string] $Ssid
[string] $Version
[string] $PortalPageId
[string] $PortalType
[string] $Hostname
[string] $BindCtrlId
[int] $TimeoutSec
[string] $UserId
[SecureString] $Passwd
[string] $Vlan
[string] $WlanUserIp
[string] $MacAddress
[string] $Rand
[int] $SuccessCode = 0
[int] $UserNotFoundCode = 1
[int] $IllegalAccessCode = 44
NetworkConfig() {
$this.BaseURL = ""
$this.WlanAcName = ""
$this.WlanAcIp = ""
$this.Ssid = ""
$this.Version = "0"
$this.PortalPageId = "3"
$this.PortalType = "0"
$this.Hostname = $env:COMPUTERNAME
$this.BindCtrlId = ""
$this.TimeoutSec = 30
$this.UserId = ""
$this.Passwd = $null
$this.Vlan = ""
$this.WlanUserIp = ""
$this.MacAddress = ""
$this.Rand = ""
}
}
class DomainConfig {
static [hashtable] GetDomainOptions() {
return @{
"1" = "@xxgcyd"
"2" = "@xxgclt"
"3" = "@xxgcdx"
}
}
static [string] GetDomainName([string]$suffix) {
if ($suffix -eq "@xxgcyd") { return "移动" }
if ($suffix -eq "@xxgclt") { return "联通" }
if ($suffix -eq "@xxgcdx") { return "电信" }
return "未知"
}
}
class ConfigManager {
[string] $ConfigFilePath
ConfigManager([string]$path) {
$this.ConfigFilePath = $path
}
[hashtable] LoadConfig() {
if (-not (Test-Path $this.ConfigFilePath)) { return $null }
try {
$content = Get-Content -Path $this.ConfigFilePath -Raw -ErrorAction Stop
if ([string]::IsNullOrWhiteSpace($content)) { return $null }
$config = ConvertFrom-Json $content -ErrorAction Stop
$requiredFields = @("BaseURL", "WlanAcName", "WlanUserIp", "MacAddress", "Vlan")
foreach ($field in $requiredFields) {
if (-not $config.PSObject.Properties.Name.Contains($field)) {
return $null
}
}
$password = ""
if ($config.PSObject.Properties.Name.Contains('EncryptedPassword')) {
$secureString = ConvertTo-SecureString -String $config.EncryptedPassword -ErrorAction Stop
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString)
$password = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)
[Runtime.InteropServices.Marshal]::FreeBSTR($bstr)
}
return @{
BaseURL = $config.BaseURL
WlanAcName = $config.WlanAcName
WlanAcIp = if ($config.PSObject.Properties.Name.Contains('WlanAcIp')) { $config.WlanAcIp } else { "" }
Ssid = if ($config.PSObject.Properties.Name.Contains('Ssid')) { $config.Ssid } else { "" }
Version = if ($config.PSObject.Properties.Name.Contains('Version')) { $config.Version } else { "0" }
PortalPageId = if ($config.PSObject.Properties.Name.Contains('PortalPageId')) { $config.PortalPageId } else { "3" }
PortalType = if ($config.PSObject.Properties.Name.Contains('PortalType')) { $config.PortalType } else { "0" }
Hostname = if ($config.PSObject.Properties.Name.Contains('Hostname')) { $config.Hostname } else { $env:COMPUTERNAME }
BindCtrlId = if ($config.PSObject.Properties.Name.Contains('BindCtrlId')) { $config.BindCtrlId } else { "" }
UserId = if ($config.PSObject.Properties.Name.Contains('UserId')) { $config.UserId } else { "" }
Password = $password
Vlan = $config.Vlan
WlanUserIp = $config.WlanUserIp
MacAddress = $config.MacAddress
Rand = if ($config.PSObject.Properties.Name.Contains('Rand')) { $config.Rand } else { "" }
}
}
catch {
Write-Host "读取配置文件失败: $($_.Exception.Message)" -ForegroundColor Yellow
return $null
}
}
[void] SaveConfig([NetworkConfig]$config, [string]$password) {
try {
$securePassword = ConvertTo-SecureString -String $password -AsPlainText -Force
$encryptedPassword = ConvertFrom-SecureString -SecureString $securePassword
$configObject = [PSCustomObject]@{
BaseURL = $config.BaseURL
WlanAcName = $config.WlanAcName
WlanAcIp = $config.WlanAcIp
Ssid = $config.Ssid
Version = $config.Version
PortalPageId = $config.PortalPageId
PortalType = $config.PortalType
Hostname = $config.Hostname
BindCtrlId = $config.BindCtrlId
UserId = $config.UserId
EncryptedPassword = $encryptedPassword
Vlan = $config.Vlan
WlanUserIp = $config.WlanUserIp
MacAddress = $config.MacAddress
Rand = $config.Rand
}
$dir = Split-Path $this.ConfigFilePath -Parent
if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
$json = $configObject | ConvertTo-Json -Compress
Set-Content -Path $this.ConfigFilePath -Value $json -Encoding UTF8 -NoNewline
(Get-Item $this.ConfigFilePath).Attributes = [System.IO.FileAttributes]::Hidden
Write-Host "配置已保存到: $this.ConfigFilePath" -ForegroundColor Green
}
catch {
Write-Host "保存配置失败: $($_.Exception.Message)" -ForegroundColor Red
}
}
}
class NetworkInterfaceHelper {
[string] GetWirelessMacAddress() {
try {
$ad = Get-NetAdapter | Where-Object {
($_.InterfaceDescription -match 'Wi-Fi|Wireless|WLAN') -and
$_.Status -eq 'Up' -and
$_.Name -notmatch 'Virtual|VMware|Hyper-V|VirtualBox'
} | Select-Object -First 1
if ($ad) {
$mac = ($ad.MacAddress -replace '[-:]', ':').ToLower()
if ($mac -notmatch '^([0-9a-f]{2}:){5}[0-9a-f]{2}$') {
$mac = ($ad.MacAddress -replace '[-.]', ':').ToLower()
}
return $mac
}
} catch {}
return $null
}
[string] GetWifiIpAddress() {
try {
$ad = Get-NetAdapter | Where-Object {
($_.InterfaceDescription -match 'Wi-Fi|Wireless|WLAN') -and
$_.Status -eq 'Up' -and
$_.Name -notmatch 'Virtual|VMware|Hyper-V|VirtualBox'
} | Select-Object -First 1
if ($ad) {
$ip = Get-NetIPAddress -InterfaceIndex $ad.IfIndex -AddressFamily IPv4 -ErrorAction Stop
return $ip.IPAddress
}
} catch {}
return $null
}
}
class RedirectUrlParser {
static [hashtable] ParseRedirectUrl([string]$url) {
$result = @{}
try {
$decodedUrl = [Uri]::UnescapeDataString($url)
if ($decodedUrl -match '^(http://[^/]+(/\w+\.do))') {
$result['BaseURL'] = $matches[1]
}
if ($decodedUrl -match '\?(.*)$') {
$queryString = $matches[1]
$params = $queryString -split '&'
foreach ($param in $params) {
$kv = $param -split '=', 2
if ($kv.Length -eq 2) {
$key = $kv[0]
$value = [Uri]::UnescapeDataString($kv[1])
switch ($key) {
"wlanuserip" { $result['WlanUserIp'] = $value }
"wlanacname" { $result['WlanAcName'] = $value }
"wlanacIp" { $result['WlanAcIp'] = $value }
"mac" { $result['MacAddress'] = $value.ToLower() }
"vlan" { $result['Vlan'] = $value }
"hostname" { $result['Hostname'] = $value }
"rand" { $result['Rand'] = $value }
"url" { $result['Url'] = $value }
}
}
}
}
return $result
}
catch {
Write-Host "解析URL失败: $($_.Exception.Message)" -ForegroundColor Red
return $null
}
}
}
class AuthenticationClient {
[NetworkConfig] $Config
[ConfigManager] $ConfigMgr
[NetworkInterfaceHelper] $NetworkHelper
[bool] $IsAutoDetected
AuthenticationClient([NetworkConfig]$c, [ConfigManager]$cm, [NetworkInterfaceHelper]$nh) {
$this.Config = $c
$this.ConfigMgr = $cm
$this.NetworkHelper = $nh
$this.IsAutoDetected = $false
}
[void] Run() {
Write-Host "`n===== 新乡工程学院校园网登录脚本 =====" -ForegroundColor Cyan
$savedConfig = $this.ConfigMgr.LoadConfig()
if ($savedConfig) {
Write-Host "`n已找到保存的配置,自动登录中..." -ForegroundColor Cyan
$this.ApplyConfig($savedConfig)
if (-not [string]::IsNullOrEmpty($savedConfig['Password'])) {
$this.Config.Passwd = ConvertTo-SecureString $savedConfig['Password'] -AsPlainText -Force
}
$this.PerformAuthentication()
return
}
Write-Host "`n=== 步骤1:自动获取登录参数 ===" -ForegroundColor Yellow
Write-Host "正在尝试通过重定向获取登录信息..." -ForegroundColor White
$autoParams = $this.TryAutoDetectParams()
if ($autoParams) {
$this.ApplyConfig($autoParams)
$this.IsAutoDetected = $true
Write-Host "自动获取成功!" -ForegroundColor Green
$this.DisplayNetworkInfo()
} else {
Write-Host "`n请按以下步骤操作:" -ForegroundColor White
Write-Host "1. 断开当前Wi-Fi重新连接" -ForegroundColor Gray
Write-Host "2. 打开浏览器访问任意网站(如 www.qq.com)" -ForegroundColor Gray
Write-Host "3. 浏览器会自动重定向到登录页面" -ForegroundColor Gray
Write-Host "4. 复制浏览器地址栏中的完整URL地址" -ForegroundColor Gray
Write-Host "5. 将URL粘贴到下面" -ForegroundColor Gray
$manualUrl = Read-Host "`n请粘贴重定向URL"
while ([string]::IsNullOrWhiteSpace($manualUrl) -or $manualUrl -notmatch '^http://' -or $manualUrl -notmatch '/portal\.do') {
Write-Host "URL格式不正确,请输入包含 /portal.do 的重定向URL" -ForegroundColor Red
$manualUrl = Read-Host "请重新粘贴重定向URL"
}
$manualParams = [RedirectUrlParser]::ParseRedirectUrl($manualUrl)
if ($manualParams -and $manualParams['WlanUserIp']) {
$this.ApplyConfig($manualParams)
Write-Host "URL解析成功!" -ForegroundColor Green
$this.DisplayNetworkInfo()
} else {
Write-Host "URL解析失败,请检查URL是否完整" -ForegroundColor Red
Read-Host "按Enter退出"
exit 1
}
}
$this.PromptForCredentials()
Write-Host "`n=== 是否保存配置? ===" -ForegroundColor Yellow
$save = Read-Host "保存后下次无需重新输入 (y/N)"
if ($save -eq 'y' -or $save -eq 'Y') {
$b = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($this.Config.Passwd)
$plainPwd = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($b)
[Runtime.InteropServices.Marshal]::FreeBSTR($b)
$this.ConfigMgr.SaveConfig($this.Config, $plainPwd)
}
$this.PerformAuthentication()
}
[hashtable] TryAutoDetectParams() {
$response = $null
try {
Write-Host "方法1: 尝试访问 www.qq.com 捕获重定向..." -ForegroundColor Gray
try {
$response = Invoke-WebRequest -Uri "http://www.qq.com" -MaximumRedirection 0 -ErrorAction SilentlyContinue
}
catch {
$response = $null
}
if ($response -and $response.Headers["Location"]) {
$redirectUrl = $response.Headers["Location"]
Write-Host "捕获到重定向: $redirectUrl" -ForegroundColor Gray
return [RedirectUrlParser]::ParseRedirectUrl($redirectUrl)
}
}
catch {
Write-Host "方法1失败: $($_.Exception.Message)" -ForegroundColor Gray
}
try {
Write-Host "方法2: 尝试直接访问校园网portal..." -ForegroundColor Gray
$localIp = $this.NetworkHelper.GetWifiIpAddress()
$localMac = $this.NetworkHelper.GetWirelessMacAddress()
if (-not $localIp) {
Write-Host "无法获取本地IP,请确保已连接Wi-Fi" -ForegroundColor Red
return $null
}
$response = $null
try {
$response = Invoke-WebRequest -Uri "http://172.18.252.12:6060" -MaximumRedirection 0 -TimeoutSec 10 -ErrorAction SilentlyContinue
}
catch {
$response = $null
}
if ($response -and $response.Headers["Location"]) {
$redirectUrl = $response.Headers["Location"]
Write-Host "捕获到重定向: $redirectUrl" -ForegroundColor Gray
$params = [RedirectUrlParser]::ParseRedirectUrl($redirectUrl)
if ($params) {
if (-not $params['WlanUserIp']) { $params['WlanUserIp'] = $localIp }
if (-not $params['MacAddress']) { $params['MacAddress'] = $localMac }
return $params
}
}
}
catch {
Write-Host "方法2失败: $($_.Exception.Message)" -ForegroundColor Gray
}
return $null
}
[void] ApplyConfig([hashtable]$params) {
if ($params['BaseURL']) { $this.Config.BaseURL = $params['BaseURL'] }
if ($params['WlanAcName']) { $this.Config.WlanAcName = $params['WlanAcName'] }
if ($params['WlanAcIp']) { $this.Config.WlanAcIp = $params['WlanAcIp'] }
if ($params['Ssid']) { $this.Config.Ssid = $params['Ssid'] }
if ($params['Version']) { $this.Config.Version = $params['Version'] }
if ($params['PortalPageId']) { $this.Config.PortalPageId = $params['PortalPageId'] }
if ($params['PortalType']) { $this.Config.PortalType = $params['PortalType'] }
if ($params['Hostname']) { $this.Config.Hostname = $params['Hostname'] }
if ($params['BindCtrlId']) { $this.Config.BindCtrlId = $params['BindCtrlId'] }
if ($params['UserId']) { $this.Config.UserId = $params['UserId'] }
if ($params['Vlan']) { $this.Config.Vlan = $params['Vlan'] }
if ($params['WlanUserIp']) { $this.Config.WlanUserIp = $params['WlanUserIp'] }
if ($params['MacAddress']) { $this.Config.MacAddress = $params['MacAddress'] }
if ($params['Rand']) { $this.Config.Rand = $params['Rand'] }
$localIp = $this.NetworkHelper.GetWifiIpAddress()
$localMac = $this.NetworkHelper.GetWirelessMacAddress()
if ($localIp) { $this.Config.WlanUserIp = $localIp }
if ($localMac) { $this.Config.MacAddress = $localMac }
}
[void] DisplayNetworkInfo() {
Write-Host "`n--- 当前网络信息 ---" -ForegroundColor Cyan
Write-Host " 认证地址: $($this.Config.BaseURL)" -ForegroundColor White
Write-Host " AC名称: $($this.Config.WlanAcName)" -ForegroundColor White
Write-Host " 用户IP: $($this.Config.WlanUserIp)" -ForegroundColor White
Write-Host " MAC地址: $($this.Config.MacAddress)" -ForegroundColor White
Write-Host " VLAN: $($this.Config.Vlan)" -ForegroundColor White
Write-Host " 主机名: $($this.Config.Hostname)" -ForegroundColor White
}
[void] PromptForCredentials() {
Write-Host "`n=== 步骤2:输入账号信息 ===" -ForegroundColor Yellow
Write-Host "请选择运营商:" -ForegroundColor White
$opt = [DomainConfig]::GetDomainOptions()
foreach ($k in $opt.Keys | Sort-Object) {
$name = [DomainConfig]::GetDomainName($opt[$k])
Write-Host " $k. $name ($($opt[$k]))"
}
$v = ""
do {
$v = Read-Host "请输入对应数字 (1/2/3)"
} while ($opt.Keys -notcontains $v)
$choice = $v
$suffix = $opt[$choice]
$operatorName = [DomainConfig]::GetDomainName($suffix)
$id = Read-Host "请输入学号(纯数字)"
while ($id -notmatch '^\d+$' -or [string]::IsNullOrWhiteSpace($id)) {
Write-Host "学号必须是纯数字!" -ForegroundColor Red
$id = Read-Host "请重新输入学号"
}
$this.Config.UserId = $id + $suffix
Write-Host "完整账号: $($this.Config.UserId) ($operatorName)" -ForegroundColor Cyan
do {
$p1 = Read-Host "请输入校园网密码" -AsSecureString
$p2 = Read-Host "请再次输入密码确认" -AsSecureString
$b1 = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($p1)
$s1 = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($b1)
[Runtime.InteropServices.Marshal]::FreeBSTR($b1)
$b2 = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($p2)
$s2 = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($b2)
[Runtime.InteropServices.Marshal]::FreeBSTR($b2)
if ($s1 -eq $s2 -and -not [string]::IsNullOrWhiteSpace($s1)) {
$this.Config.Passwd = $p1
break
} elseif ([string]::IsNullOrWhiteSpace($s1)) {
Write-Host "密码不能为空!" -ForegroundColor Red
} else {
Write-Host "两次输入的密码不一致!" -ForegroundColor Red
}
} while ($true)
}
[void] PerformAuthentication() {
Write-Host "`n=== 步骤3:开始认证 ===" -ForegroundColor Yellow
Write-Host "MAC地址: $($this.Config.MacAddress)" -ForegroundColor Gray
$b = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($this.Config.Passwd)
$plainPwd = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($b)
try {
$uuid = [guid]::NewGuid().ToString()
$timestamp = [int](Get-Date -UFormat %s) * 1000
$authUrl = $this.Config.BaseURL -replace '/\w+\.do', '/quickauth.do'
$queryParams = @(
"userid=$([Uri]::EscapeDataString($this.Config.UserId))",
"passwd=$([Uri]::EscapeDataString($plainPwd))",
"wlanuserip=$($this.Config.WlanUserIp)",
"wlanacname=$([Uri]::EscapeDataString($this.Config.WlanAcName))",
"wlanacIp=$($this.Config.WlanAcIp)",
"ssid=$($this.Config.Ssid)",
"vlan=$($this.Config.Vlan)",
"mac=$($this.Config.MacAddress)",
"version=$($this.Config.Version)",
"portalpageid=$($this.Config.PortalPageId)",
"timestamp=$timestamp",
"uuid=$uuid",
"portaltype=$($this.Config.PortalType)",
"hostname=$([Uri]::EscapeDataString($this.Config.Hostname))",
"bindCtrlId=$($this.Config.BindCtrlId)"
) -join "&"
$requestUrl = $authUrl + "?" + $queryParams
Write-Host "请求地址: $requestUrl" -ForegroundColor Gray
$response = Invoke-WebRequest -Uri $requestUrl -Method Get -UseBasicParsing -TimeoutSec 15 -ErrorAction Stop
Write-Host "`n=== 认证响应 ===" -ForegroundColor Cyan
Write-Host "HTTP状态码: $($response.StatusCode)" -ForegroundColor Green
Write-Host "响应内容: $($response.Content)" -ForegroundColor White
if ($response.Content -match '"code"\s*:\s*0' -or $response.Content -match "success" -or $response.Content -match "认证成功") {
Write-Host "`n认证成功!您已连接到互联网。" -ForegroundColor Green
Write-Host "账号: $($this.Config.UserId)" -ForegroundColor Cyan
exit 0
} elseif ($response.Content -match '"code"\s*:\s*1' -or $response.Content -match "账号不存在") {
Write-Host "`n认证失败:账号不存在,请检查学号和运营商是否正确" -ForegroundColor Red
} elseif ($response.Content -match '"code"\s*:\s*44' -or $response.Content -match "非法接入") {
Write-Host "`n认证失败:非法接入,请检查VLAN ID或MAC地址是否正确" -ForegroundColor Red
} else {
Write-Host "`n认证结果未知,请检查账号密码是否正确" -ForegroundColor Yellow
Write-Host "响应: $($response.Content)" -ForegroundColor Gray
}
}
catch {
Write-Host "`n认证请求发送失败!" -ForegroundColor Red
Write-Host "错误信息: $($_.Exception.Message)" -ForegroundColor DarkGray
}
finally {
if ($b) { [Runtime.InteropServices.Marshal]::FreeBSTR($b) }
}
Read-Host "`n按 Enter 键退出脚本" | Out-Null
}
}
try {
$config = [NetworkConfig]::new()
$configMgr = [ConfigManager]::new((Join-Path $env:APPDATA "xxgc_campus_net_config.txt"))
$netHelper = [NetworkInterfaceHelper]::new()
$authClient = [AuthenticationClient]::new($config, $configMgr, $netHelper)
$authClient.Run()
}
catch {
Write-Host "`n脚本执行出错:$($_.Exception.Message)" -ForegroundColor Red
Write-Host "详细错误:$($_.ScriptStackTrace)" -ForegroundColor DarkGray
Read-Host "按 Enter 键退出"
exit 1
} bat脚本(给不想右键运行的懒蛋用,需要跟ps1在同一目录下,同时ps1文件名应该为xywdl.ps1。可以自己改。):
@echo off
setlocal
powershell.exe -ExecutionPolicy Bypass -File xywdl.ps1
set "EXIT_CODE=!ERRORLEVEL!"
if !EXIT_CODE! EQU 0 (
exit /b 0
)
效果如图:



回车即可连接成果,之后运行程序便会自动连接完成,如果要修改登录信息,需要去C:\Users\<当前用户名>\AppData\Roaming\xxgc_campus_net_config.txt删掉这个txt文件。
免责声明:
本人郑重声明:
此脚本及相关的技术研究、分析、编写和测试过程,纯粹出于个人学习、研究和探索的目的,旨在深入理解网络协议(特别是HTTP请求)、PowerShell编程语言、Windows操作系统网络配置管理以及特定场景下的Web认证机制(即校园网Portal认证)的工作原理与实现细节。
学习目的:本项目的首要且唯一目的是进行技术学习和知识积累。通过实际动手实践,加深对编程、网络通信、自动化脚本等相关技术领域的理解和掌握。此脚本的诞生过程,是我个人求知欲驱动下的一次技术探索,绝非为了任何商业用途、盈利行为或恶意攻击。
研究性质:对校园网认证流程的分析,属于对公开可访问的网络服务交互过程的研究。这种研究仅限于观察、理解和模拟合法用户在正常使用网络服务时的行为,其本质是对标准HTTP GET请求及其参数构成的解析与重构,是一种中立的技术探讨。
无恶意意图:本人明确表示,对该认证流程的了解和此脚本的编写,不包含任何破坏网络服务、窃取他人信息、规避合理收费、干扰正常网络管理秩序或进行任何违法、违规操作的意图。脚本的设计初衷仅是为了简化本人作为合法授权用户的日常登录操作,提高效率。
遵守规则:本人深知并尊重所在教育机构制定的网络使用规定和相关法律法规。此脚本的使用范围严格限定于本人拥有合法授权的账户,并仅在指定的网络环境中进行测试。任何关于滥用此脚本进行非授权访问、批量注册、资源滥用或其他违反校规或法律的行为,均与本人无关,也绝非本人所愿见。
非工具推广:发布或分享此脚本的相关信息,同样仅限于技术交流和学习分享的范畴。它并非一款面向大众的成品工具,不具备广泛的普适性和健壮性,其代码和逻辑可能高度依赖于特定的网络环境(如脚本中硬编码的服务器地址、AC名称等)。使用者应自行承担因环境差异导致的一切后果。
责任归属:本人不对任何第三方使用此脚本或基于此脚本进行二次开发、修改、传播后产生的任何直接或间接后果承担责任。任何个人或组织若选择参考、借鉴或使用此脚本,其行为及结果均属其自主决定,与本人无涉。使用者应自行评估风险,并确保其行为符合当地法律法规及所在机构的政策。
知识产权:虽然此脚本是本人原创,但其中涉及的校园网认证系统、所使用的Windows API、PowerShell运行环境、HTTP协议等均为相关方的成果。本脚本的创作并未侵犯任何第三方的知识产权,仅是在合法授权范围内对公开接口的正当利用。
持续学习:技术的学习是一个持续的过程,本项目也代表了我在特定阶段的认识水平。随着技术的发展和认知的深化,未来可能会有更优的解决方案或不同的理解角度,但这不影响当前声明的有效性。
综上所述,再次强调,此项研究及成果展示,是纯粹的、严肃的、仅限于学术和技术层面的个人学习行为。感谢您的理解。