关于学校强智教务系统的登录流程解析

SKKK 发布于 6 天前 27 次阅读


前言

强智教务系统是国内高校广泛使用的教务管理系统,广泛应用于课程表查询、成绩管理、考试安排等场景。因为我要开发一个课表软件,所以进行了研究。本文将对强智教务系统的登录流程进行详细的技术分析,揭示其密码加密算法的实现原理。

一、登录流程分析

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说明
1GET/获取 JSESSIONID Cookie
2POST/Logon.do?method=logon&flag=sess获取加密参数
3GET/verifycode.servlet获取验证码图片
4POST/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 秒

八、总结

本文分析了强智教务系统的登录流程和密码加密算法。通过还原加密算法的核心逻辑,我们可以实现对该系统的自动化登录,从而进一步开发课表查询、成绩查询等扩展功能。需要注意的是,在实际使用中应当遵守相关法律法规,合理使用自动化工具。