[Auto CQUPT Plan] 校园网相关接口(补档)

2699 字
13 分钟
[Auto CQUPT Plan] 校园网相关接口(补档)

基于 CQUPT-Net-SDK 项目,按其源码整理。本 SDK 通过 Go 语言封装了重邮校园网的登录、登出、状态检测、上网信息查询、上线记录查询、在线设备查询等能力。

Warning

此接口仅供学习使用,请勿用于非法用途,否则造成的危害后果自负。

1. 通用说明#

1.1 两类接口#

本 SDK 涉及两类输入参数不同、请求地址不同的接口:

类型说明参数
Portal 认证接口192.168.200.2:801校园网接入网关,负责设备的认证/登出/状态检测,返回 JSONP 格式数据LoginData
自服务接口202.202.32.120:8443校园网自服务门户(/Self/*),需要先通过网页表单登录获取 Cookie,负责账号信息/记录查询LoginData

1.2 公共参数(LoginData#

字段说明取值
StuID统一认证码(学号)字符串
Password密码字符串,明文
UA设备类型,决定 User-Agent 与认证设备号desktopphonepad
Isp运营商telecom(电信),cmcc(移动),unicom(联通),xyw(教师)
IPAddr设备 IP需填局域网内真实 IP
MACAddr路由器 MAC,冒号会被自动去除全部置 "000000000000" 时由认证服务器自动识别

登录时不一定要在路由器上执行本 SDK,只需在路由器局域网内任意一台设备上执行即可。

2. 接口列表#

#接口(SDK 方法)功能请求地址
1NetLogin校园网登录认证http://192.168.200.2:801/eportal/ (GET, JSONP)
2NetLogout校园网注销http://192.168.200.2:801/eportal/ (GET, JSONP)
3NetChecker认证状态检测http://192.168.200.2:801/eportal/ (GET, JSONP)
4LoginCquptService自服务门户登录(内部)https://202.202.32.120:8443/Self/login/verify (POST, 表单)
5GetNetServiceInfo获取上网服务信息https://202.202.32.120:8443/Self/dashboard (GET, 域名解析)
6GetLoginHistory获取登录记录https://202.202.32.120:8443/Self/dashboard/getLoginHistory (GET, JSON)
7GetOnlineList获取当前在线设备列表https://202.202.32.120:8443/Self/dashboard/getOnlineList (GET, JSON)

4~7 为自服务接口,内部会先通过 LoginCquptService 获取并缓存(15 秒)一个有效 Cookie,再用该 Cookie 请求。


3. Portal 认证接口(网关,192.168.200.2:801#

公共请求头:

Host: 192.168.200.2:801
Referer: http://192.168.200.2/
User-Agent: <按 UA 类型映射>

3.1 校园网登录 — NetLogin#

  • 请求方式:GET
  • JSONP 方法名:dr1003

Query 参数:

参数说明
c固定 Portal
a固定 login
callback固定 dr1003
login_method固定 1
user_account,{device},{StuID}@{Isp}devicedesktop=0,phone/pad=1
user_password明文密码
wlan_user_ip设备 IP
wlan_user_macMAC 去除冒号后大写
jsVersion固定 3.3.3

响应(JSONP 回调包裹的 JSON):

字段类型说明
resultstring0 表示成功
msgstring提示信息
ret_codeint返回码,2 表示当前设备已认证

解析时会对原始响应做 body[7:len-1] 截断以去掉 JSONP 外层,再反序列化为 JSON。当 result==0 且 ret_code==2 时,SDK 会返回 当前设备已认证

net/http 直接调用示例:

func netLogin(stuID, password, isp, ip, mac, ua string) {
device := "0" // desktop 为 0,phone/pad 为 1
if ua != "desktop" {
device = "1"
}
// 构造 query 参数
u, _ := url.Parse("http://192.168.200.2:801/eportal/")
q := u.Query()
q.Set("c", "Portal")
q.Set("a", "login")
q.Set("callback", "dr1003")
q.Set("login_method", "1")
q.Set("user_account", ","+device+","+stuID+"@"+isp)
q.Set("user_password", password)
q.Set("wlan_user_ip", ip)
q.Set("wlan_user_mac", strings.ReplaceAll(mac, ":", ""))
q.Set("jsVersion", "3.3.3")
u.RawQuery = q.Encode()
req, _ := http.NewRequest(http.MethodGet, u.String(), nil)
req.Header.Set("Host", "192.168.200.2:801")
req.Header.Set("Referer", "http://192.168.200.2/")
req.Header.Set("User-Agent", ua)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
s := string(body) // dr1003({"result":"0","msg":"...","ret_code":0})
s = s[7 : len(s)-1] // 去掉 JSONP 外壳
var r struct {
Result string `json:"result"`
Msg string `json:"msg"`
RetCode int `json:"ret_code"`
}
json.Unmarshal([]byte(s), &r)
fmt.Printf("result=%s msg=%s ret_code=%d\n", r.Result, r.Msg, r.RetCode)
}

3.2 校园网注销 — NetLogout#

  • 请求方式:GET
  • JSONP 方法名:dr1002
  • 动作标识:a=unbind_mac

Query 参数:

参数说明
c固定 Portal
a固定 unbind_mac
callback固定 dr1002
user_account{StuID}@{Isp}
wlan_user_macMAC(去冒号)
wlan_user_ip设备 IP
jsVersion固定 3.3.3
v随机数(1000~9999)

响应(JSONP):

字段类型说明
resultstring结果
msgstring提示信息

net/http 直接调用示例:

func netLogout(stuID, isp, ip, mac, ua string) {
u, _ := url.Parse("http://192.168.200.2:801/eportal/")
q := u.Query()
q.Set("c", "Portal")
q.Set("a", "unbind_mac")
q.Set("callback", "dr1002")
q.Set("user_account", stuID+"@"+isp)
q.Set("wlan_user_mac", strings.ReplaceAll(mac, ":", ""))
q.Set("wlan_user_ip", ip)
q.Set("jsVersion", "3.3.3")
q.Set("v", fmt.Sprintf("%d", rand.Intn(9000)+1000)) // 随机数
u.RawQuery = q.Encode()
req, _ := http.NewRequest(http.MethodGet, u.String(), nil)
req.Header.Set("Host", "192.168.200.2:801")
req.Header.Set("Referer", "http://192.168.200.2/")
req.Header.Set("User-Agent", ua)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
s := string(body)[7 : len(body)-1] // 去 JSONP 外壳
var r struct {
Result string `json:"result"`
Msg string `json:"msg"`
}
json.Unmarshal([]byte(s), &r)
fmt.Printf("result=%s msg=%s\n", r.Result, r.Msg)
}

3.3 认证状态检测 — NetChecker#

  • 请求方式:GET
  • JSONP 方法名:dr1001
  • 动作标识:a=page_type_data

Query 参数:

参数说明
c固定 Portal
a固定 page_type_data
callback固定 dr1001
v随机数(1000~9999)

响应(JSONP):

字段类型说明
resultstring认证状态
timestring时间
msgstring提示信息

net/http 直接调用示例:

func netChecker(ua string) {
u, _ := url.Parse("http://192.168.200.2:801/eportal/")
q := u.Query()
q.Set("c", "Portal")
q.Set("a", "page_type_data")
q.Set("callback", "dr1001")
q.Set("v", fmt.Sprintf("%d", rand.Intn(9000)+1000)) // 随机数
u.RawQuery = q.Encode()
req, _ := http.NewRequest(http.MethodGet, u.String(), nil)
req.Header.Set("Host", "192.168.200.2:801")
req.Header.Set("Referer", "http://192.168.200.2/")
req.Header.Set("User-Agent", ua)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
s := string(body)[7 : len(body)-1] // 去 JSONP 外壳
var r struct {
Result string `json:"result"`
Time string `json:"time"`
Msg string `json:"msg"`
}
json.Unmarshal([]byte(s), &r)
fmt.Printf("result=%s time=%s msg=%s\n", r.Result, r.Time, r.Msg)
}

4. 自服务门户接口(202.202.32.120:8443#

自服务接口分为两段流程:

  1. 登录获取 CookieLoginCquptService 触发的三步流程(见 §4.1);
  2. 携带 Cookie 查询:用上一步的 Cookie 请求各类数据接口(见 §4.2 / §4.3 / §4.4)。

Cookie 会被缓存 15 秒,过期后自动重新登录。

公共请求头(自服务接口):

Cookie: <登录后获得的会话Cookie>
User-Agent: <按 UA 类型映射>
X-Requested-With: XMLHttpRequest

4.1 自服务登录(内部流程)#

仅用于获取会话 Cookie,供后续查询使用。完整流程为:

步骤请求说明
① 打开登录页GET https://202.202.32.120:8443/Self/login/?302=LI抓取 Set-Cookie 与隐藏表单字段 checkcode
② 激活 CookiePOST https://202.202.32.120:8443/Self/login/randomCode?t={rand}Content-Type: application/x-www-form-urlencoded,刷新超时
③ 提交登录POST https://202.202.32.120:8443/Self/login/verify携带 ② 的 Cookie 提交账号密码,成功后重定向到 /Self/dashboard

第 ③ 步 表单参数:

参数说明
foo
bar
checkcode页面隐藏值
account学号(StuID)
password密码
code

成功判定: 响应头 Location == "/Self/dashboard"。其他值视为登录失败。

net/http 直接调用示例(三步流程):

// ① 打开登录页,抓取 Cookie 与隐藏字段 checkcode
loginReq, _ := http.NewRequest("GET", "https://202.202.32.120:8443/Self/login/?302=LI", nil)
loginReq.Header.Set("User-Agent", ua)
loginResp, err := client.Do(loginReq) // client 见 §5,已关闭自签名证书校验并禁用自动重定向
if err != nil {
log.Fatal(err)
}
cookie := loginResp.Header.Get("Set-Cookie") // 取第一段,如 "JSESSIONID=..."
s, _ := io.ReadAll(loginResp.Body) // 从 HTML 的 <input type="hidden" name="checkcode" value="..."> 里正则提取 checkcode
loginResp.Body.Close()
// ② 激活 Cookie(刷新超时)
actReq, _ := http.NewRequest("POST",
"https://202.202.32.120:8443/Self/login/randomCode?t=0.5562441101050084", nil)
actReq.Header.Set("Cookie", cookie)
client.Do(actReq)
// ③ 提交登录,表单校验成功会返回 Location: /Self/dashboard
vals := url.Values{}
vals.Set("foo", "")
vals.Set("bar", "")
vals.Set("checkcode", checkcode) // 第 ① 步抓取的值
vals.Set("account", stuID)
vals.Set("password", password)
vals.Set("code", "")
verifyReq, _ := http.NewRequest("POST",
"https://202.202.32.120:8443/Self/login/verify",
strings.NewReader(vals.Encode()))
verifyReq.Header.Set("Cookie", cookie)
verifyReq.Header.Set("User-Agent", ua)
verifyReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
verifyReq.Header.Set("X-Requested-With", "XMLHttpRequest")
verifyResp, err := client.Do(verifyReq)
if err != nil {
log.Fatal(err)
}
// verifyResp.Header.Get("Location") 若非 "/Self/dashboard" 则登录失败
fmt.Printf("会话 Cookie: %s\n", cookie)

4.2 获取上网服务信息 — GetNetServiceInfo#

  • 请求:GET https://202.202.32.120:8443/Self/dashboard
  • 使用 HTML 解析器(colly)抓取页面 DOM,非 JSON 返回。

返回字段(NetInfoS):

字段JSON key说明
UserNameuser_name姓名
UsedTimeused_time已用时长
UsedFlowused_flow已用流量
Balancebalance账户余额
Accountaccount学号
Statusstatus账号状态
Packagepackage用户类型/套餐
PackageDetailpackageDetail套餐详情
BillingTypebillingType计费方式
BillingCyclebillingCycle计费周期

net/http 直接调用示例(需先自服务登录拿到 Cookie):

req, _ := http.NewRequest("GET", "https://202.202.32.120:8443/Self/dashboard", nil)
req.Header.Set("Cookie", cookie) // 自服务登录获取的会话 Cookie
req.Header.Set("User-Agent", ua)
req.Header.Set("X-Requested-With", "XMLHttpRequest")
resp, err := client.Do(req) // client 见 §5
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
// 返回的是 HTML 页面,需解析 DOM(如 colly / goquery):
// div.thumbnail div.caption h4 → 姓名
// div.user-info1 dl 中 dd=已用时长/已用流量/账户余额 对应对应的 dt
// div.panel-body div.row 中 label=账号/状态/套餐/计费方式/计费周期 对应 span
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))

4.3 获取登录记录 — GetLoginHistory#

  • 请求:GET https://202.202.32.120:8443/Self/dashboard/getLoginHistory
  • Accept: application/json, text/javascript, */*; q=0.01
  • 返回为二维数组 JSON,每行 ≥12 个元素,按列映射为一条登录记录。

返回字段(LoginHistoryRecordS):

字段JSON key说明数组下标
LoginTimeloginTime上线时间(毫秒时间戳)0
LogoutTimelogoutTime注销时间(毫秒时间戳)1
IPipIP 地址2
MACmacMAC(大写无缝)3
UseTimeuseTime使用时长(分钟)4
UseFlowuseFlow使用流量(M)5
BillingTypebillingType计费方式(1时长/2流量/3包月)6
ChargeAmountchargeAmount计费金额7
HostNamehostName主机名8
TerminalTypeterminalType终端类型(PC/移动终端)10

net/http 直接调用示例(需先自服务登录拿到 Cookie):

req, _ := http.NewRequest("GET",
"https://202.202.32.120:8443/Self/dashboard/getLoginHistory", nil)
req.Header.Set("Cookie", cookie)
req.Header.Set("User-Agent", ua)
req.Header.Set("X-Requested-With", "XMLHttpRequest")
req.Header.Set("Accept", "application/json, text/javascript, */*; q=0.01")
resp, err := client.Do(req) // client 见 §5
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
// 返回为二维数组 JSON,每行 ≥12 个元素,按下标映射(0 上线时间,1 注销时间,...)
var rows [][]interface{}
json.NewDecoder(resp.Body).Decode(&rows)
for _, r := range rows {
if len(r) < 12 {
continue
}
// r[0]/r[1] 为毫秒时间戳,r[2]=IP, r[3]=MAC, r[4]=时长(分), r[5]=流量(M)
log.Printf("loginTime=%v ip=%v useTime=%v\n", r[0], r[2], r[4])
}

4.4 获取当前在线列表 — GetOnlineList#

  • 请求:GET https://202.202.32.120:8443/Self/dashboard/getOnlineList?t={rand}&order=asc&_={ms}
  • Accept: application/json, text/javascript, */*; q=0.01
  • Content-Type: application/json
  • 返回为 JSON 对象数组。

返回字段(OnlineRecord):

字段JSON key说明
BrasidbrasidBRAS ID
DownFlowdownFlow下行流量(KB,推测)
HostNamehostName主机名
IPipIP 地址
LoginTimeloginTime上线时间(YYYY-MM-DD HH:MM:SS
MACmacMAC(无分隔符)
SessionIdsessionId会话 ID
TerminalTypeterminalType终端类型(可能带 # 前缀)
UpFlowupFlow上行流量
UseTimeuseTime使用时长(秒)
UserIduserId用户 ID

注:在线列表返回值中的流量/时长为字符串,SDK 会再做 ParseInt/Atoi 转成数字。

net/http 直接调用示例(需先自服务登录拿到 Cookie):

// 带随机参数与时间戳(用于规避缓存)
u, _ := url.Parse("https://202.202.32.120:8443/Self/dashboard/getOnlineList")
q := u.Query()
q.Set("t", strconv.FormatFloat(rand.Float64(), 'f', -1, 64))
q.Set("order", "asc")
q.Set("_", fmt.Sprintf("%d", time.Now().UnixMilli()))
u.RawQuery = q.Encode()
req, _ := http.NewRequest("GET", u.String(), nil)
req.Header.Set("Cookie", cookie) // 自服务登录获取的会话 Cookie
req.Header.Set("User-Agent", ua)
req.Header.Set("X-Requested-With", "XMLHttpRequest")
req.Header.Set("Accept", "application/json, text/javascript, */*; q=0.01")
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req) // client 见 §5
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
// 返回为 JSON 对象数组;流量/时长为字符串,需 ParseInt/Atoi 转数字
var list []struct {
IP string `json:"ip"`
MAC string `json:"mac"`
UpFlow string `json:"upFlow"`
DownFlow string `json:"downFlow"`
UseTime string `json:"useTime"`
LoginTime string `json:"loginTime"` // "YYYY-MM-DD HH:MM:SS"
}
json.NewDecoder(resp.Body).Decode(&list)
for _, item := range list {
fmt.Printf("%s | %s | up=%s down=%s\n", item.IP, item.MAC, item.UpFlow, item.DownFlow)
}

5. 公共 HTTP 客户端配置#

自服务接口使用自签名的 HTTPS 证书,且登录流程依赖读取 Location 重定向头(不允许自动跟随)。因此用 net/http 直接调用前,需要先配置一个关闭 TLS 证书校验、禁用自动重定向http.Client

client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 关闭证书校验(自签名 HTTPS)
},
// 关闭自动重定向,以便读取登录后响应头里的 Location 判断成败
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}

所有示例中统一使用该 client 而非 http.DefaultClient。除特殊说明外,请求都需要设置请求头 User-Agent(按 §1.2 的 UA 类型取值),门户接口额外带上 X-Requested-With: XMLHttpRequest

各接口的完整请求构造见前文各处示例:

  • Portal 认证:见 §3.1 / §3.2 / §3.3 —— 直接 GET,解析 JSONP 响应。
  • 自服务登录:见 §4.1 —— 先 GET 登录页拿 Cookie,再 POST 提交。
  • 自服务查询:见 §4.2 / §4.3 / §4.4 —— 携带上一步的 Cookie 请求。

由于关闭了自动重定向,自服务登录成功后,需通过响应头 Location 手动判断是否跳转到 /Self/dashboard


6. 附注:请求地址常量#

见项目 core/spyder/consts.go

192.168.200.2:801 # 网关 Portal 主机
202.202.32.120:8443 # 自服务门户主机
http://192.168.200.2:801/eportal/ # Portal 认证接口(登录/登出/检测)
https://202.202.32.120:8443/Self/login/?302=LI # 自服务登录页
https://202.202.32.120:8443/Self/login/verify # 登录提交
https://202.202.32.120:8443/Self/login/randomCode?t=... # Cookie 激活
https://202.202.32.120:8443/Self/dashboard # 上网信息页
https://202.202.32.120:8443/Self/dashboard/getLoginHistory # 登录记录
https://202.202.32.120:8443/Self/dashboard/getOnlineList # 在线列表

注:SDK 中 TLS 证书校验被关闭(InsecureSkipVerify),并关闭了 HTTP 自动重定向,以便解析登录跳转。

7. 相关项目#

Auto-CQUPT-Plan
/
CQUPT-Net-SDK
Waiting for api.github.com...
00K
0K
0K
Waiting...

8. 结尾#

没有了喵,谢谢阅读

gopher
gopher

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

[Auto CQUPT Plan] 校园网相关接口(补档)
https://blog.tuf3i.cc/posts/acp-campus-net/
作者
TuF3i
发布于
2026-08-18
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
TuF3i
一只区,什么也不想写
分类
标签
站点统计
文章
16
分类
9
标签
18
总字数
43,299
运行时长
0
最后活动
0 天前

目录