前言
强智教务系统是国内高校广泛使用的教务管理系统,广泛应用于课程表查询、成绩管理、考试安排等场景。因为我要开发一个课表软件,所以进行了研究。本文将对强智教务系统的登录流程进行详细的技术分析,揭示其密码加密算法的实现原理。
一、登录流程分析
1.1 完整登录时序图
整个登录流程分为以下5个步骤:
- Step 1: GET / — 获取 JSESSIONID Cookie
- Step 2: POST /Logon.do?method=logon&flag=sess — 获取加密参数 scode#sxh
- Step 3: 本地执行加密算法 encode(用户名, 密码, scode, sxh)
- Step 4: GET /verifycode.servlet — 获取验证码图片
- Step 5: POST /Logon.do?method=logon — 提交登录表单
1.2 登录参数说明
| 步骤 | 请求方法 | URL | 说明 |
|---|---|---|---|
| 1 | GET | / | 获取 JSESSIONID Cookie |
| 2 | POST | /Logon.do?method=logon&flag=sess | 获取加密参数 |
| 3 | GET | /verifycode.servlet | 获取验证码图片 |
| 4 | POST | /Logon.do?method=logon | 提交登录表单 |
二、核心加密算法详解
2.1 算法原理
强智教务系统采用了一种变种字符插入加密算法,其核心思想如下:
- 构造原始字符串:将用户名和密码用
%%%连接,格式为{username}%%%{password} - 动态字符插入:对于原始字符串的前20个字符,在每个字符后面插入 scode 的一部分
- 插入长度控制:每次插入的长度由 sxh 字符串对应位置的数字决定(1-3之间)
- 剩余字符直接追加:超过20个字符后,直接追加剩余内容
2.2 JavaScript 源码分析
以下是该加密算法的原始 JavaScript 实现:
function loginajax() {
var strUrl = "/Logon.do?method=logon&flag=sess";
$.ajax({
url: strUrl,
type: "post",
cache: false,
dataType: "text",
success: function(dataStr) {
if (dataStr == "no") {
return false;
} else {
var scode = dataStr.split("#")[0];
var sxh = dataStr.split("#")[1];
var code = document.getElementById("userAccount").value + "%%%" +
document.getElementById("userPassword").value;
var encoded = "";
for (var i = 0; i < code.length; i++) {
if (i < 20) {
encoded = encoded + code.substring(i, i + 1) +
scode.substring(0, parseInt(sxh.substring(i, i + 1)));
scode = scode.substring(parseInt(sxh.substring(i, i + 1)), scode.length);
} else {
encoded = encoded + code.substring(i, code.length);
i = code.length;
}
}
document.getElementById("encoded").value = encoded;
document.getElementById("userPassword").value = "";
document.getElementById("loginForm").submit();
}
}
});
}
2.3 Python 实现
以下是 Python 版本的加密算法实现:
def encode_password(username: str, password: str, scode: str, sxh: str) -> str:
"""
强智教务系统密码加密算法
Args:
username: 用户名/学号
password: 密码
scode: 服务器返回的加密种子(随机字符串)
sxh: 服务器返回的位置索引(固定20个数字字符)
Returns:
加密后的字符串
"""
code = f"{username}%%%{password}"
encoded = ""
for i in range(len(code)):
if i < 20:
encoded += code[i]
n = int(sxh[i])
encoded += scode[:n]
scode = scode[n:]
else:
encoded += code[i:]
break
return encoded
2.4 加密示例演示
输入参数:
username = "1145141919810"
password = "mypassword"
scode = "X8E6q3nU4i79ZC262QY52atdu1rd9X4Rehk89"
sxh = "32231121223122121321"
# 构造原始字符串
code = "1145141919810%%%mypassword" # 长度: 26
# 逐字符加密
i=0: '1' + scode[:3]='X8E' → "1X8E", scode='6q3nU4i79ZC262QY52atdu1rd9X4Rehk89'
i=1: '1' + scode[:2]='6q' → "1X8E16q", scode='3nU4i79ZC262QY52atdu1rd9X4Rehk89'
i=2: '4' + scode[:2]='3n' → "1X8E16q43n", scode='U4i79ZC262QY52atdu1rd9X4Rehk89'
...
# 最终输出:
encoded = "1X8E16q43n5U144i7199ZC12692QY8512a0td%u%1r%dm9X4yRephak89ssword"
三、API 接口规范
3.1 获取加密参数
POST /Logon.do?method=logon&flag=sess
响应格式: "scode#sxh"
示例: "X8E6q3nU4i79ZC262QY52atdu1rd9X4Rehk89#32231121223122121321"
3.2 获取验证码
GET /verifycode.servlet
响应: PNG 图片数据 (4位字母数字验证码)
3.3 提交登录
POST /Logon.do?method=logon
Content-Type: application/x-www-form-urlencoded
表单参数:
userAccount : 用户名(明文)
userPassword : 空字符串
encoded : 加密后的字符串
RANDOMCODE : 验证码
成功响应: HTTP 302 → /jsxsd/framework/xsMainV.htmlx
四、错误处理
4.1 常见错误代码
| 错误信息 | 原因 | 解决方案 |
|---|---|---|
| 该帐号不存在或密码错误,请联系管理员! | 用户名或密码错误 | 检查凭据 |
| 验证码错误!! | 验证码识别错误 | 重新识别 |
| 请安装加密狗插件,插入加密狗! | 验证码为空 | 确保验证码已填写 |
4.2 错误信息提取
import re
def extract_error(html: str) -> str:
patterns = [
r'id=["']showMsg["'][^>]*>([^<]+)<',
r']*color=["']?red["']?[^>]*>([^<]+)',
]
for pattern in patterns:
match = re.search(pattern, html, re.IGNORECASE)
if match:
return match.group(1).strip()
return "未知错误"
五、完整登录实现
import requests
import re
from urllib.parse import urljoin
from PIL import Image
import ddddocr
class QZLogin:
def __init__(self, base_url: str):
self.base_url = base_url.rstrip("/")
self.session = requests.Session()
self.ocr = ddddocr.DdddOcr()
def _encode(self, username, password, scode, sxh):
code = f"{username}%%%{password}"
encoded = ""
for i in range(len(code)):
if i < 20:
encoded += code[i]
n = int(sxh[i])
encoded += scode[:n]
scode = scode[n:]
else:
encoded += code[i:]
break
return encoded
def login(self, username, password, max_retry=10):
for attempt in range(max_retry):
self.session.get(self.base_url)
resp = self.session.post(
self.base_url + "/Logon.do?method=logon&flag=sess"
)
scode, sxh = resp.text.strip().split("#", 1)
encoded = self._encode(username, password, scode, sxh)
resp = self.session.get(self.base_url + "/verifycode.servlet")
captcha = self.ocr.classification(resp.content)
resp = self.session.post(
self.base_url + "/Logon.do?method=logon",
data={
"userAccount": username,
"userPassword": "",
"encoded": encoded,
"RANDOMCODE": captcha,
},
allow_redirects=True
)
if "main" in resp.url.lower():
return True
return False
六、依赖安装
pip install requests ddddocr pillow
七、注意事项
- 验证码识别率约 70-90%,建议设置数次重试以达到 99% 成功率
- 登录成功后必须保持同一 Session 进行后续请求
- JSESSIONID 有时效性,长时间不操作需重新登录
- 避免频繁请求,建议每次请求间隔 1-2 秒
八、总结
本文分析了强智教务系统的登录流程和密码加密算法。通过还原加密算法的核心逻辑,我们可以实现对该系统的自动化登录,从而进一步开发课表查询、成绩查询等扩展功能。需要注意的是,在实际使用中应当遵守相关法律法规,合理使用自动化工具。

Comments NOTHING