起因
逛 Github 看到 南大家园 的项目,
就想着 诶 我们学校官网 能不能 扒出来什么好玩的深入分析学习 一下网站(确信)
说干就干,第一步当然是实现 模拟登录 了
于是就有了这个:
Github 项目地址: dogxii/zzuli-node-login
基于 Nodejs 实现了 登录二维码生成,获取钥匙(JSESSIONID)去访问一些需要用户认证的接口。
分析登录
-
第一步当然是访问 官网 的登录页面进行
Cookie 分析了在 登录页面 右键进入检查,点开 应用 - Cookie,

可以看到 Cookie 这里设置了 client 和 JSESSIONID,path 为 /cas/,
这时候就可以基本判断为 Java 实现的
CAS 单点登录了。(不过我当时并没有判断出来,因为我根本不知道 CAS 是什么,经过后面的分析才得知 QAQ)
下面步骤省略 n 张图…
-
打开
网络可以看到有持续的casSweepCodeLoginQuery的请求,用于判断登录二维码状态 -
在
元素页面找到 刷新二维码 标签,看到标签的点击事件绑定了 createQrCode() 函数:<span onclick="createQrCode()" ...>刷新二维码</span>在
控制台输入 createQrCode 获取到定义,点击找到相应的 js 位置(可以看到是直接写入页面 scirpt 中的/** * 生成二维码 */ function createQrCode() { var uuid = getUuid() clearInterval(num) token = '' $('#qrLogin').empty() $('#qrLogin').qrcode({ render: 'canvas', width: 200, height: 200, text: location.origin + '/cas/openAuth?uuid=' + uuid, }) $('#qrLogin').show() $('.successTishi').hide() $('.ewmModal').hide() // TODO 启动获取状态 2分钟内每秒执行一次 getAuthorizationJob(uuid) }-
由上面代码分析出来二维码生成流程:生成
随机 uuid,用qrcode()生成对应 https://kys.zzuli.edu.cn/cas/openAuth?uuid=xxxx 链接的二维码。 -
一步一步找相关代码,复刻二维码
轮询:
/** * 生成uuid */ function getUuid() { // xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx return 'xxxx4xxxyxxxxxxx'.replace(/[xy]/g, function (c) { var r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8 return v.toString(16) }) } /** * 获取认证信息 */ function getAuthorization(uuid) { $.ajax({ url: 'casSweepCodeLoginQueryController', type: 'POST', data: { uuid: uuid, token: token, }, success: function (jsonObj) { var json = JSON.parse(jsonObj) // 等待扫码 if (json.success && json.obj.code == 'waitSweep') { // 每次重置token token = json.obj.token } // 等待授权 else if (json.success && json.obj.code == 'waitAuthorized') { // 每次重置token token = json.obj.token // 增加已扫码效果 $('.successTishi').show() $('#qrLogin').hide() $('#qrLogin').empty() } // 已授权 else if (json.success && json.obj.code == 'alreadyAuthorized') { // 刷新页面并跳转至service clearInterval(num) if ('' == '' || $.trim('').length <= 0) { window.location.href = location.origin + '/cas/login' } else { window.location.href = location.origin + '/cas/login?service=' + '' } } // 已取消 else if (json.success && json.obj.code == 'cancel') { // 增加遮罩层二维码失效 clearInterval(num) $('.ewmModal p').text('授权已取消') $('.ewmModal').show() } // 已失效 else if (json.success && json.obj.code == 'invalid') { // 增加遮罩层二维码失效 clearInterval(num) $('.ewmModal p').text('授权已失效') $('.ewmModal').show() } // 不合法 else if (json.success && json.obj.code == 'error') { // 增加遮罩层二维码失效 clearInterval(num) $('.ewmModal p').text('授权已失效') $('.ewmModal').show() } }, error: function (msg) { // 增加遮罩层二维码失效 clearInterval(num) $('.ewmModal p').text('授权已失效') $('.ewmModal').show() }, }) } // ... ...具体过程不阐述。6. 二维码轮询返回
alreadyAuthorized后,跳转到 CAS 登录验证界面 https://kys.zzuli.edu.cn/cas/login?service=xxxx ,这里service为需授权域名登录地址,请求后302 重定向地址并附带ticke参数:GET https://kys.zzuli.edu.cn/cas/login?service=https://campus.zzuli.edu.cn/portal-pc/login/pcLogin Response: 302 Found Location: https://campus.zzuli.edu.cn/portal-pc/login/pcLogin?ticket=ST-108096-xxxxx-jinghua- 携带
ticket请求后再次302 重定向到首页,获取到最终JSESSIONID:
GET https://campus.zzuli.edu.cn/portal-pc/login/pcLogin?ticket=ST-108096-xxxxx-jinghua Response: 302 Found Set-Cookie: JSESSIONID=13113A3D836F74E055B66A92925D8343; Path=/portal-pc; HttpOnly Location: https://campus.zzuli.edu.cn/portal-pc/login/pcLogin好像还有 CASTGC 等凭证?文章隔了很长时间再写 有些忘记了 =x=
-
访问 API
这里只提供一个获取学生信息的 API(QAQ),
携带 JSESSIONID 发送 GET 请求
GET https://kys.zzuli.edu.cn/authentication/member/getUserInfo
Cookie: JSESSIONID=77EF782EAD98CD505AE9E8FCA480A81A
返回数据:
{
"success": true,
"msg": "操作成功",
"obj": {
"memberId": "542500010101",
"memberUsername": "542500010101",
"memberPwd": null,
"memberNickname": "奶龙",
"memberSex": 1,
"memberPhone": "114****14",
"memberIdNumber": "419****1111",
"memberCreateTime": 1725420135000,
"memberState": 1,
"memberAcademicNumber": "542500010101",
"memberMailbox": "54****0101@email.zzuli.edu.cn",
"memberSign": "1",
"memberImage": null,
"memberOtherSchoolNumber": "542500010101",
"memberOtherNation": "龙族",
"memberOtherDepartment": "奶龙学院",
"memberOtherMajor": "奶龙技术",
"memberOtherGrade": "2025",
"memberOtherClass": "奶龙班25-01",
"memberOtherBirthday": "2008-08-08",
"memberOtherNative": null,
"lastLoginTime": "2025年05月28日 22:17",
"quicklyTicket": null,
"roleCodeList": ["student"],
"roleList": [
{
"roleCode": "student",
"roleName": "学生",
"roleState": null,
"roleComment": null
}
],
"deptList": [
{
"dptCode": "000210",
"dptName": "奶龙学院",
"dptAbbreviation": null,
"dptCategoryCode": null,
"dptEngName": null,
"dptBelong": null,
"dptLevel": null,
"dptSetOtherCode": null,
"dptSetUpYear": null,
"dptOriginalStandardCode": null,
"dptStartTime": null,
"dptEndTime": null,
"dptSort": null,
"dptState": null,
"dptPublishState": null,
"children": null,
"memberList": null,
"title": "奶龙学院",
"name": "奶龙学院"
}
],
"deptCodeList": ["000210"],
"memberIdAesEncrypt": "wxnSLWezwtresLZSfA%3D%3D",
"memberAesEncrypt": "tWwL3dawe32csdzZHNKw%3D%3D",
"memberCasLastLoginTime": null,
"memberAppLastLoginTime": null,
"memberPcLastLoginTime": "2025年05月28日 22:17",
"memberSalt": null,
"memberUpdatePasswordTime": null,
"memberIdNumberSign": null
},
"attributes": null,
"count": null
}
本文到此结束 =w= 这篇文章好水!
有错误及疑问 欢迎评论区指出
让我们 下期再见(X
加载中...