让 AI 管理你的云盘：OpenList Skill 开源项目分享当 AI Agent 遇上自建网盘，文件管理变得像对话一样简单。前言自建网盘用户大多听过 AList——那个能聚合几十种存储后端的文件列表程序。而 OpenList 作为 AList 的社区分支，在保持 API 兼容的同时，坚持开源治理和社区驱动。如果你在用 OpenList 或 AList 自建网盘，并且也在用 AI Agent 辅助工作，那今天分享的这个项目可能会让你眼前一亮：OpenList Skill —— 一个让 AI Agent 直接操作你网盘的技能包。它解决什么问题？日常使用网盘，我们习惯了打开浏览器 → 登录 → 点击文件夹 → 找文件 → 下载/分享这一串操作。但如果 AI 能直接帮你完成这些事呢？"帮我找一下云盘里那个报告 PDF""把备份目录下的照片打包分享链接""把本地的日志文件上传到云盘的归档目录"OpenList Skill 就是让这些自然语言指令变成现实。它封装了 OpenList/AList 的完整 HTTP API，提供 21 个命令，覆盖文件浏览、上传下载、搜索、批量重命名、分享管理等场景。功能全景📁 文件操作（11 个命令）命令做什么示例login登录并保存 token一次登录，后续自动鉴权list列出目录内容浏览任意文件夹get获取文件/目录详情查看大小、修改时间等search搜索文件关键词模糊查找mkdir创建文件夹新建归档目录rename重命名整理文件命名move移动文件文件归类整理copy复制文件跨目录备份remove删除文件清理过期文件link获取下载直链拿到文件的真实下载地址upload上传本地文件将本地文件推到云端⚡ 批量操作（2 个命令）命令做什么batch-rename批量重命名：old1:new1,old2:new2regex-rename正则替换重命名：所有 .txt → .md正则重命名是效率神器。比如想把一整季视频文件从 S01E01_1080p.mp4 统一改成 第1集.mp4，一条正则命令搞定。🔗 分享管理（5 个命令）命令做什么share-list查看所有分享share-create创建分享链接（可设密码、过期时间）share-get查看分享详情share-update修改分享设置share-delete删除分享分享链接格式为 http://<host>/@s/<id>，创建后直接返回完整链接，一步到位。🔍 索引管理（4 个命令）命令做什么index-build全量重建搜索索引（慎重使用）index-update增量更新索引index-clear清除索引index-progress查看索引构建进度亮点设计1. 智能搜索：API + 递归遍历双保险这是我觉得最用心的设计。搜索文件时，Skill 会先尝试 OpenList 的 API 搜索；如果索引未建立或关键词过滤失效（部分存储后端不支持），它会自动降级为递归目录遍历，确保总能找到文件。# API 搜索优先，失败时自动回退遍历 python scripts/openlist.py search --path /Backup --keyword "报告" # 也可以跳过 API，直接用遍历模式 python scripts/openlist.py search --path /Backup --keyword "报告" --no-api更贴心的是，Skill 还内置了一个搜索无结果处理流程，防止 AI Agent 动不动就触发耗时的全量索引重建：换关键词再试（模糊匹配、部分文件名）用 list 浏览目录手动定位增量更新索引 index-update --paths "/目标目录"只有索引损坏或首次配置时才 index-build2. 零配置快速上手两种认证方式，按需选择：自动登录（推荐）——一次配置，后续无忧：python scripts/openlist.py login \ --url https://your-openlist.example.com \ --username admin \ --password your-password自动生成 config.json，后续所有命令自动读取凭据。命令行参数——临时使用或脚本集成：python scripts/openlist.py list \ --url https://your-openlist.example.com \ --token eyJhbGci... \ --path /3. 单文件架构，极简部署整个核心功能就一个 openlist.py 文件（约 600 行），加上一个 SKILL.md 技能描述文件和一份 API 参考文档。没有复杂的项目结构，没有层层依赖，pip install requests 就能跑。openlist-skill/ ├── SKILL.md # Agent 技能描述 ├── scripts/ │ └── openlist.py # 核心——唯一入口 ├── references/ │ └── api.md # 完整 API 参考 └── config.json # 自动生成，gitignored4. 标准化 JSON 输出所有命令返回统一的 JSON 格式，方便 Agent 解析和程序化调用：// 成功 {"code": 200, "message": "success", "data": {...}} // 失败 {"code": 400, "message": "error description", "data": null}还支持 --json（纯 JSON）和 --quiet（精简输出）两种模式，适配不同使用场景。实战场景场景一：对话式文件管理你：帮我看看云盘根目录有什么 AI：执行 list --path / → 返回目录列表 你：把报告上传到 /Backup/2026/ AI：执行 upload --path /Backup/2026/report.pdf --file ./report.pdf 你：给这个文件创建一个分享链接，7天后过期 AI：执行 share-create --files /Backup/2026/report.pdf --expire-hours 168 → 返回分享链接场景二：批量文件整理# 把一目录下的 .JPG 文件统一改成 .jpg python scripts/openlist.py regex-rename \ --src-dir /Photos/2026 \ --src-regex "^(.*)\.JPG$" \ --dst-regex "$1.jpg"场景三：定时备份脚本# 上传今天的日志，覆盖已有文件 python scripts/openlist.py upload \ --path /Logs/app.log \ --file /var/log/app.log \ --replace与 OpenList 的关系OpenList 是 AList 的社区分支，API 完全兼容 AList v3。这意味着：✅ OpenList 用户：原生支持✅ AList v3 用户：同样适用，无需任何修改OpenList Skill 只需要你的实例地址和登录凭据，就能工作。快速开始# 1. 克隆项目 git clone https://github.com/drfccv/OpenList-skill.git # 2. 安装依赖 pip install requests # 3. 登录你的 OpenList/AList 实例 python scripts/openlist.py login \ --url https://your-instance.example.com \ --username admin \ --password secret # 4. 开始使用 python scripts/openlist.py list --path /写在最后OpenList Skill 的价值不在于它有多复杂——恰恰相反，它的价值在于简单。600 行 Python，21 个命令，把网盘操作变成了 AI Agent 可以直接调用的能力。对于自建网盘用户来说，这意味着你的 AI 助手不再只是"会聊天"——它真的能帮你管理文件了。如果你也在用 OpenList 或 AList，不妨试试：🔗 GitHub: https://github.com/drfccv/OpenList-skill📦 License: MIT欢迎 Star ⭐、提 Issue、提 PR！
Agent Skills开发教程本教程基于Skill Creator核心指南编写，将详细讲解Agent Skills（以下简称“Skill”）的定义、核心原则、结构组成、设计理念及完整开发流程，帮助你快速掌握Skill开发方法，打造可扩展Claude能力的专业化模块。一、Skill基础认知（About Skills）Skill是模块化、独立的功能包，通过提供专业知识、工作流程和工具集成，扩展Claude的核心能力。你可以将其理解为“特定领域的入职指南”——它能让通用型Claude转变为具备专属流程知识的专业代理，这些流程知识是通用模型无法完全掌握的。1.1 Skill的核心价值专业工作流程：针对特定领域的多步骤操作流程（如PDF处理、API调用流程等）。工具集成能力：提供特定文件格式（如DOCX、PDF）或API的操作说明。领域专业知识：包含公司专属知识、数据 schema、业务逻辑等定制化内容。捆绑资源包：整合脚本、参考文档、资产文件等，支撑复杂重复任务的高效执行。二、Skill开发核心原则（Core Principles）遵循以下原则可确保Skill的高效性、易用性和上下文友好性，是开发优质Skill的基础。2.1 简洁为王（Concise is Key）上下文窗口是宝贵的公共资源，Skill需与系统提示、对话历史、其他Skill元数据及用户请求共享该窗口。开发时需牢记：默认假设“Claude已足够智能”，仅添加Claude不具备的上下文信息。对每段信息进行核验：“Claude真的需要这段解释吗？”“这段内容的Token成本是否合理？”优先使用简洁示例，而非冗长说明。2.2 设置适当自由度（Set Appropriate Degrees of Freedom）根据任务的脆弱性和可变性，匹配对应的指令 specificity 级别，如同为Claude规划行动路径——悬崖边的窄桥需严格护栏（低自由度），开阔田野可自由探索（高自由度）。高自由度（文本型指令）：适用于多种方案均有效、决策依赖上下文或需启发式引导的场景（如创意文案优化、通用咨询）。中自由度（伪代码/带参数脚本）：适用于有优选模式、允许适度变体或配置影响行为的场景（如带参数的数据分析脚本）。低自由度（特定脚本/少参数）：适用于操作脆弱易出错、一致性至关重要或需严格遵循固定序列的场景（如系统部署步骤、敏感数据处理）。三、Skill的结构组成（Anatomy of a Skill）每个Skill必须包含SKILL.md文件（必需），可选择性包含捆绑资源。标准目录结构如下： skill-name/ ├── SKILL.md (必需) │ ├── YAML前置元数据 (必需) │ │ ├── name: (必需) │ │ └── description: (必需) │ └── Markdown指令 (必需) └── 捆绑资源 (可选) ├── scripts/ - 可执行代码（Python/Bash等） ├── references/ - 按需加载的参考文档 └── assets/ - 输出用资源（模板、图标等）3.1 必需组件：SKILL.md该文件是Skill的核心，包含Claude识别和使用Skill所需的全部关键信息，分为前置元数据和正文两部分。3.1.1 前置元数据（YAML格式）仅包含两个必需字段，是Claude判断Skill用途和触发时机的核心依据，需清晰、全面描述。name：Skill名称（简洁唯一，如“pdf-editor”“bigquery-skill”）。description：Skill描述（核心触发机制），需同时说明“Skill功能”和“触发场景”，避免冗余。示例：“Comprehensive document creation, editing, and analysis with support for tracked changes. Use when Claude needs to work with .docx files for creating/editing documents or handling tracked changes.”注意：禁止添加其他YAML字段，仅保留name和description。3.1.2 正文（Markdown格式）包含Skill使用的指令和指南，仅在Skill触发后才会加载。写作需遵循“命令式/不定式语气”（如“Extract text with pdfplumber”），聚焦核心流程，避免无关信息。3.2 可选组件：捆绑资源根据Skill需求选择性添加，用于存储可复用内容，减少正文冗余并提升执行效率。3.2.1 scripts/（可执行脚本目录）存储Python、Bash等可执行代码，适用于“重复执行”或“需确定性可靠性”的任务（如PDF旋转、数据清洗）。使用场景：相同代码需重复编写、任务要求结果可确定。示例：scripts/rotate_pdf.py（PDF旋转脚本）。注意：脚本需实际运行测试，确保无bug；若存在多个相似脚本，可抽样测试以平衡效率。3.2.2 references/（参考文档目录）存储需按需加载的参考资料，用于支撑Claude的决策和操作（如数据 schema、API文档、公司政策）。使用场景：文档内容较详细、仅特定场景需用到。示例：references/schema.md（数据库表结构）、references/api_docs.md（API说明）。最佳实践：① 大型文件（>10k字）需在SKILL.md中添加grep搜索模式；② 信息仅存于一处（SKILL.md或参考文件，不重复）；③ 仅将核心流程保留在SKILL.md，详细内容迁移至参考文件。3.2.3 assets/（输出资产目录）存储用于“最终输出”的文件，不加载到上下文（如模板、图标、字体、前端脚手架）。使用场景：Skill输出需依赖固定资源（如生成PPT需模板、输出文档需logo）。示例：assets/logo.png（品牌图标）、assets/frontend-template/（React项目模板）。3.3 禁止包含的文件Skill仅需包含支撑功能的核心文件，禁止添加无关文档，包括但不限于：README.md、INSTALLATION_GUIDE.md、QUICK_REFERENCE.md、CHANGELOG.md等。此类文件会增加冗余并造成混淆。四、渐进式披露设计原则（Progressive Disclosure Design Principle）该原则通过“三级加载机制”高效管理上下文，避免Skill占用过多Token，核心是“按需加载、最小化上下文”。4.1 三级加载系统元数据（name + description）：始终在上下文（约100词，占用极少Token）。SKILL.md正文：Skill触发时加载（建议<5k字，控制在500行内）。捆绑资源：Claude根据需求按需加载（无字数限制，脚本可执行无需加载上下文）。4.2 三种核心模式当SKILL.md正文接近500行限制时，需拆分内容至参考文件，并在正文中明确引用，确保Claude知晓资源位置和使用时机。模式1：高级指南+参考文件正文保留核心流程，高级功能链接至对应参考文件，示例： # PDF Processing ## Quick start Extract text with pdfplumber: [简单代码示例] ## Advanced features - **Form filling**: See [FORMS.md](FORMS.md) for complete guide - **API reference**: See [REFERENCE.md](REFERENCE.md) for all methods - **Examples**: See [EXAMPLES.md](EXAMPLES.md) for common patterns模式2：按领域组织资源多领域Skill按领域拆分参考文件，避免加载无关内容，示例目录结构： bigquery-skill/ ├── SKILL.md (概述+导航) └── reference/ ├── finance.md (财务指标) ├── sales.md (销售数据) ├── product.md (产品API) └── marketing.md (营销活动)模式3：条件化细节展示正文展示基础内容，高级细节链接至参考文件，示例： # DOCX Processing ## Creating documents Use docx-js for new documents. See [DOCX-JS.md](DOCX-JS.md). ## Editing documents For simple edits, modify the XML directly. **For tracked changes**: See [REDLINING.md](REDLINING.md) **For OOXML details**: See [OOXML.md](OOXML.md)关键注意事项参考文件需直接链接自SKILL.md，避免深层嵌套。长参考文件（>100行）需在顶部添加目录，方便Claude快速预览范围。五、Skill开发完整流程（Skill Creation Process）Skill开发需遵循以下6个步骤，按顺序执行（无明确理由不得跳过），确保Skill质量和可用性。步骤1：通过具体示例理解Skill需求仅当Skill用途和使用模式已完全明确时可跳过，即使优化现有Skill，此步骤也有参考价值。核心是通过“具体示例”明确Skill功能和触发场景。获取示例：直接收集用户真实使用案例，或生成示例后经用户验证。核心问题（以高中语文作文评分SKILL为例）：- Skill需支持哪些功能？（作文评分，点评，改进建议等）f - 用户会如何触发Skill？（给出一篇作文，要求打分等） 注意：避免一次性提问过多，优先聚焦核心问题，逐步跟进补充。完成标志：清晰掌握Skill需支持的所有功能和触发场景。步骤2：规划可复用Skill内容分析步骤1中的具体示例，提炼可复用资源（脚本、参考文件、资产），将“重复任务”转化为“可复用组件”，降低后续维护成本。示例分析：PDF编辑Skill：旋转PDF需重复编写代码 → 规划scripts/rotate_pdf.py。前端项目构建Skill：创建项目需重复编写HTML/React模板 → 规划assets/frontend-template/。BigQuery查询Skill：需反复查阅表结构 → 规划references/schema.md。高中语文作文评分SKILL：需要有一个作文评分标准规划一个评分标准.md需要一个字数统计的py脚本输出：可复用资源清单（明确每个资源的类型、用途和存储路径）。步骤3：初始化Skill（Initialize the Skill）仅当Skill已存在（需迭代或打包）时可跳过，新Skill必须通过初始化脚本创建，确保目录结构标准化，提升开发效率。核心操作：运行init_skill.py脚本，生成标准化Skill模板。 scripts/init_skill.py <skill-name> --path <output-directory>脚本功能：在指定路径创建Skill目录（skill-name）。生成带前置元数据和TODO占位符的SKILL.md模板。创建scripts/、references/、assets/目录及示例文件。后续操作：根据需求自定义或删除生成的模板文件（示例文件仅用于展示结构，非必需）。步骤4：编辑Skill（Edit the Skill）基于初始化模板，实现可复用资源并完善SKILL.md，核心是“为Claude编写指南”——需提供非显而易见但有价值的信息（流程、注意事项、资源位置）。4.1 优先实现可复用资源根据步骤2的规划，编写scripts/、references/、assets/中的内容：脚本：编写后需运行测试，确保无bug。参考文件：按领域/功能分类，添加目录（长文件）。资产：放入输出所需的模板、图标等文件。注意：删除无需使用的示例文件（如Skill不涉及脚本，可删除scripts/目录下的示例）。4.2 完善SKILL.md前置元数据：完善name和description，确保触发场景清晰。正文：按“核心流程+资源引用”编写，使用命令式语气，链接参考文件和脚本（如“See FORMS.md for form filling guide”）。步骤5：打包Skill（Package the Skill）Skill开发完成后，需打包为可分发的.skill文件（本质是zip压缩包），打包前会自动验证Skill合法性。核心操作：运行package_skill.py脚本。 # 基础打包（输出至当前目录） scripts/package_skill.py <path/to/skill-folder> # 指定输出目录（输出至dist目录） scripts/package_skill.py <path/to/skill-folder> ./dist脚本流程：验证（Validation）：自动检查以下内容，验证失败则报错并退出：- YAML前置元数据格式及必需字段（name、description）。 - Skill命名规范和目录结构。 - description的完整性和质量。 - 文件组织和资源引用的有效性。 打包（Packaging）：验证通过后，生成以Skill名称命名的.skill文件（如pdf-editor.skill），包含所有核心文件和目录结构。注意：需修复所有验证错误后，重新运行打包命令。步骤6：迭代优化（Iterate）Skill发布后，需基于真实使用场景持续优化，解决使用中的问题和低效点，形成“使用-反馈-优化”的闭环。迭代流程：在真实任务中使用Skill。记录Skill的不足（如流程繁琐、触发不精准、资源缺失）。明确SKILL.md或捆绑资源的修改方向。实施修改并测试，重复步骤5打包发布。六、总结Agent Skills开发的核心是“简洁、高效、可复用”——通过遵循核心原则、标准化结构、渐进式披露设计和完整开发流程，可打造出能精准扩展Claude能力的专业Skill。关键在于聚焦“Claude真正需要的信息”，避免冗余，让Skill在上下文友好的前提下发挥最大价值。（注：参考来源：Anthropics）
南京工业大学统一身份认证（智慧南工）登录逆向分析
南京工业大学统一身份认证平台（SSO）逆向分析📋 目录1. 概述2. 系统架构分析3. 双密钥体系原理4. 加密算法逆向5. 前端混淆破解6. 动态参数分析7. 登录流程解析1. 概述1.1 目标系统系统名称: 南京工业大学统一身份认证平台登录地址: https://sfgl.njtech.edu.cn/cas/login认证协议: CAS (Central Authentication Service)前端框架: Angular加密库: CryptoJS1.2 核心文件文件名作用关键内容https://sfgl.njtech.edu.cn/cas/login登录页面主体密钥2、execution参数https://sfgl.njtech.edu.cn/gate/public/deploy/deploy.js脚本加载器密钥1混淆、配置请求https://sfgl.njtech.edu.cn/gate/public/utils/loginNew.js设备检测PC/Mobile判断crypto-js.min.js加密库DES/MD5/SHA256算法1.3 加密技术栈加密算法: DES 加密模式: ECB 填充方式: PKCS7 编码格式: Base642. 系统架构分析2.1 页面加载流程从 login 可以看到页面加载顺序： 2.2 脚本依赖关系从 deploy.js 第52-58行可以看到完整的依赖加载：var cptfile = [ baseUrl + '/public/convertBaseUrl.js', baseUrl + '/public/crypto/md5.min.js', baseUrl + '/public/crypto/core.min.js', baseUrl + '/public/crypto/crypto-js.min.js', baseUrl + '/public/crypto/enc-base64.min.js', baseUrl + '/public/crypto/mode-ecb.min.js', baseUrl + '/public/crypto/pad-pkcs7.min. js' ];这些文件通过 loadScript() 函数（第38-50行）依次加载。2.3 页面初始化流程从 deploy.js 第175行的 _casPageInit() 函数可以看到：function _casPageInit() { var loadedJs = []; var hasRun; function nextLoad(jsPath) { loadedJs.push(jsPath); var hasRuntime; var hasPolyfills; // 等待runtime和polyfills都加载完成 for (var i = 0; i < loadedJs.length; i++) { var path = loadedJs[i]; if (path.indexOf('runtime') !== -1) { hasRuntime = true; } else if (path.indexOf('polyfills') !== -1) { hasPolyfills = true; } } // 加载main脚本 if (hasRuntime && hasPolyfills && !hasRun) { hasRun = true; for (var i = 0; i < jsCssConfig.js.length; i++) { var jsPath = jsCssConfig.js[i]; if (jsPath.indexOf('main') !== -1) { loadOneJS(jsPath); } } } } }3. 双密钥体系原理3.1 密钥1：API通信密钥3.1.1 存储位置与混淆在 deploy.js 第124行：var url_mseAge = '_%C3%D8%AF%7C%A1%BD%BE%C2%C4%C4%92';3.1.2 解混淆函数在 deploy.js 第16-26行：var uncompile = function (code) { code = unescape(code); let c = String.fromCharCode(code.charCodeAt(0) - code.length); for (let i = 1; i < code.length; i++) { c += String.fromCharCode(code. charCodeAt(i) - c.charCodeAt(i - 1)); } return c; };算法分析：输入: '_%C3%D8%AF%7C%A1%BD%BE%C2%C4%C4%92' 步骤1: unescape() URL解码 '_%C3%D8%AF%7C%A1%BD%BE%C2%C4%C4%92' → '_ÃØ¯|¡½¾ÂÄÄ'' 步骤2: 差分解码 第一个字符: charCode(0) - length '_'. charCodeAt(0) = 95 length = 12 第一个输出: 95 - 12 = 83 → 'S' 第二个字符: charCode(1) - charCode(result[0]) 'Ã'. charCodeAt(0) = 195 result[0]. charCodeAt(0) = 83 第二个输出: 195 - 83 = 112 → 'p' 依此类推... 最终输出: 'SphG5lQmUoU='这是一种差分编码（Delta Encoding），每个字符的ASCII值是前一个字符加上一个差值。3.1.3 使用场景从 deploy.js 第130-165行的 getSelfConfig 函数可以看到：function getSelfConfig(key, cb) { let rfurl = baseUrl + '/linkid/protected/api/dictconfig/get'; // 判断是否需要加密 let hasCrypto = false; if (configUrl) { const allconfigkey = Object.keys(configUrl); for (let i = 0; i < allconfigkey.length; i++) { for (let o = 0; o < configUrl[allconfigkey[i]].length; o++) { const CurrentconfigUrl = configUrl[allconfigkey[i]][o]; if (isIncludeUrl(rfurl, CurrentconfigUrl)) { hasCrypto = true; break; } } } } // 发送加密请求 if (hasCrypto) { xhr.setRequestHeader('hasCrypto', 'true'); xhr.send(desEncrypt(uncompile(url_mseAge), JSON.stringify([key]))); } else { xhr.send(JSON.stringify([key])); } // 处理加密响应 if (! isJSONTmp) { var tmpgetunpackbody = getUnPackBody(uncompile(url_mseAge), jsonObj); } }从测试结果可以看到，密钥1保护以下API：{ "5G": [ "/data-sync/public/network/openUser/xxx", "/data-sync/public/network/cancelUser/xxx", "/data-sync/api/account/protected/tel/add", "/data-sync/api/account/tel/add", "/data-sync/api/thirdparty/tel/verify" ], "SID": [ "/linkid/api/aggregate/app/protected/obtain/", "/linkid/api/aggregate/app/protected/face/user/active/", "/linkid/protected/api/user/userId/check/", "/api/protected/realname/preQuery", "/api/protected/realname/auth", // ... 共23个接口 ] }3.2 密钥2：密码加密密钥3.2.1 存储位置在 login 第77行： B0MCcDgT4Eo= 实际访问时获得的值为 zgEBWBZtq5s=（动态生成）。3.2.2 参数提取从页面隐藏元素中可以看到完整的登录参数： UsernamePassword zgEBWBZtq5s= edd59515-9448-42e3-8f17-cb1e8abb8d9e_H4sIAA... api/captcha/generate/DEFAULT service=http%3A%2F%2Fjwgl.njtech.edu.cn%2Fsso%2Fktiotlogin%3Fstatus%3Dsuccess 3dc54c41-da03-4f06-8052-823888cc4e9d_H4sIAA... 3.3 密钥独立性验证从实际测试的登录请求可以看到：POST /cas/login username=202412345 password=TeDO/u3gzkxtgmHPJV47qA== # 使用密钥2加密 croypto=zgEBWBZtq5s= # 密钥2原样传回 type=UsernamePassword _eventId=submit execution=3dc54c41-da03-4f06-8052-823888cc4e9d_H4sIAA... 登录流程中没有使用密钥1，证明两个密钥完全独立。4. 加密算法分析4.1 加密函数定位在 deploy.js 第7-15行：var desEncrypt = function (key, content) { const keyHex = CryptoJS.enc.Base64.parse(key); const encString = CryptoJS.DES.encrypt(content, keyHex, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }); return encString.toString(); };4.2 解密函数在 deploy.js 第1-6行：var desDecrypt = function (key, content) { const keyHex = CryptoJS.enc. Base64.parse(key); const decString = CryptoJS.DES.decrypt(content, keyHex, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad. Pkcs7 }); return decString.toString(CryptoJS.enc.Utf8); };4.3 DES算法特征从 crypto-js.min.js 中可以看到DES实现的关键部分：var d = u.DES = s.extend({ _doReset: function() { // 密钥调度 for (var t = this._key.words, r = t.words, e = t.sigBytes / 4, i = this._nRounds = e + 6, n = 4 * (i + 1), s = this._keySchedule = [], a = 0; a < n; a++) // ... }, encryptBlock: function(t, r) { this._doCryptBlock(t, r, this._keySchedule, a, c, h, l, o) }, decryptBlock: function(t, r) { var e = t[r + 1]; t[r + 1] = t[r + 3], t[r + 3] = e, this._doCryptBlock(t, r, this._invKeySchedule, f, u, d, v, s); // ... } });4.4 ECB模式特征从加密配置可以看到使用了ECB模式：{ mode: CryptoJS.mode. ECB, padding: CryptoJS.pad.Pkcs7 }ECB模式的工作原理：明文: [Block1] [Block2] [Block3] ↓ ↓ ↓ DES-E DES-E DES-E ↓ ↓ ↓ 密文: [Cipher1][Cipher2][Cipher3]每个块独立加密，不依赖前后块。4.5 PKCS7填充从 crypto-js.min.js 可以看到填充实现：var _ = p. Pkcs7 = { pad: function(t, r) { for (var e = 4 * r, i = e - t.sigBytes % e, n = i << 24 | i << 16 | i << 8 | i, s = [], a = 0; a < i; a += 4) s.push(n); var c = o.create(s, i); t.concat(c) }, unpad: function(t) { var r = 255 & t.words[t.sigBytes - 1 >>> 2]; t.sigBytes -= r } };填充规则：原始数据长度 % 8 == 剩余字节数: 0字节 → 填充 08 08 08 08 08 08 08 08 1字节 → 填充 07 07 07 07 07 07 07 2字节 → 填充 06 06 06 06 06 06 ... 7字节 → 填充 014.6 加密过程验证测试输出：// 输入 密钥: zgEBWBZtq5s= 明文: test123 // 加密过程 1. Base64解码密钥: ce0101581656db9b (8字节) 2. UTF-8编码明文: 74657374313233 (7字节) 3. PKCS7填充: 7465737431323301 (8字节, 填充了0x01) 4. DES-ECB加密: 0acbdcc58742a8d4 (8字节) 5. Base64编码: CsvcxYdCqNQ= // 输出 加密结果: CsvcxYdCqNQ=5. 前端混淆破解5.1 差分编码原理5.1.1 编码过程推导从 uncompile 函数的逆向分析可以推导出编码过程：// 解码: result[i] = encoded[i] - result[i-1] // 反推编码: encoded[i] = original[i] + original[i-1] 原始字符串: S p h G 5 l Q m U o U = ASCII码: 83 112 104 71 53 108 81 109 85 111 85 61 长度: 12 编码过程: 位置0: 83 + 12 = 95 → '_' 位置1: 112 + 83 = 195 → 'Ã' (escape后 %C3) 位置2: 104 + 112 = 216 → 'Ø' (escape后 %D8) 位置3: 71 + 104 = 175 → '¯' (escape后 %AF) 位置4: 53 + 71 = 124 → '|' (escape后 %7C) 位置5: 108 + 53 = 161 → '¡' (escape后 %A1) 位置6: 81 + 108 = 189 → '½' (escape后 %BD) 位置7: 109 + 81 = 190 → '¾' (escape后 %BE) 位置8: 85 + 109 = 194 → 'Â' (escape后 %C2) 位置9: 111 + 85 = 196 → 'Ä' (escape后 %C4) 位置10: 85 + 111 = 196 → 'Ä' (escape后 %C4) 位置11: 61 + 85 = 146 → ''' (escape后 %92) 最终: '_%C3%D8%AF%7C%A1%BD%BE%C2%C4%C4%92'5.1.2 算法特征差分编码的特点：可逆性：解码函数可以完全恢复原始数据依赖性：每个字符依赖前一个字符错误传播：一个字符错误会影响后续所有字符长度保持：编码后长度不变5.2 URL编码层escape() 函数的作用：// ASCII可见字符（不编码） '_' → '_' '|' → '|' // 非ASCII字符（编码） 'Ã' (195) → '%C3' 'Ø' (216) → '%D8' '¯' (175) → '%AF'5.3 多层解密从 deploy.js 第126-145行可以看到多层解密逻辑：function getUnPackBody(key, body) { let dec = ''; let isJSON = false; const convertData = function (body) { // 开始解密 dec = desDecrypt(key, body); isJSON = false; if (dec != '') { try { JSON.parse(dec); isJSON = true; return; } catch (str_error) { isJSON = false; } // 如果解密结果不是JSON，继续解密 if (!isJSON) { convertData(dec); } } }; convertData(body); return JSON.parse(dec || null); }这说明服务器可能返回嵌套加密的数据，需要递归解密直到得到有效的JSON。6. 动态参数分析6.1 execution参数从 login 可以看到execution的格式： 格式分析：3dc54c41-da03-4f06-8052-823888cc4e9d_H4sIAAAAAAAAAO1ce2xcxRkfn9+... 组成部分: ├─ UUID部分: 3dc54c41-da03-4f06-8052-823888cc4e9d │ • 长度: 36字符 │ • 格式: 8-4-4-4-12 │ • 作用: 唯一标识本次登录流程 │ └─ 数据部分: H4sIAAAAAAAAAO1ce2wcxRkfn9+... • 格式: Base64编码 • 识别: H4sI 是Gzip压缩的Magic Number • 内容: Gzip(JSON数据)6.2 login-page-flowkey参数从 login 第80行： edd59515-9448-42e3-8f17-cb1e8abb8d9e_H4sIAAAAAAAAALU6a2xb53lH1FuWLdmWnbhZXE... 与execution格式相同：UUID_Base64GzipData6.3 验证码URL从 login 第79行： 该元素为空，但从 deploy.js 第285行可以看到检测逻辑：var captchaUrlElem = doc.getElementById('captcha-url'); if (captchaUrlElem && captchaUrlElem.innerText. trim()) { this.captchaUrl = `${this.gateUrl}/${captchaUrlElem.innerText.trim()}`; }从 loginNew.js 可以看到验证码相关代码：var ruleList_content = document.body; var d_style = null; if (window.getComputedStyle) { d_style = window.getComputedStyle(ruleList_content, null); } else { d_style = ruleList_content.currentStyle; } if (d_style. content. indexOf('x') !== -1) { if(document.getElementById('recaptcha-invisible')){ document.getElementById('recaptcha-invisible').innerText = true; document.getElementById('captcha-url').innerText = "api/captcha/generate/DEFAULT"; } }通过CSS的 content 属性判断是否需要验证码。6.4 认证类型列表从 login 第31-42行可以看到支持的认证方式： UsernamePassword 用户名密码认证 ./images/login-page/UsernamePassword.png false smsLogin 手机验证码认证 ./images/login-page/smsLogin.png false corpwechatQr 企业微信扫码认证 false 6.5 第三方认证从 login 第85-132行可以看到完整的第三方认证列表： Or login with: adapter corpWechat Wechat tyEduCloud dingDing dingDingWlan singleWechatOauth feishuOauth zhilinOauth isni ssoOauth corpWechatOauth2 welinkOauth ccwork 7. 登录流程解析7.1 请求参数完整映射从实际抓包数据：POST /cas/login HTTP/1.1 username=202412345 type=UsernamePassword _eventId=submit geolocation= execution=3dc54c41-da03-4f06-8052-823888cc4e9d_H4sIAA... captcha_code=a3d2 croypto=zgEBWBZtq5s= password=TeDO/u3gzkxtgmHPJV47qA==对应关系：表单字段来源说明username用户输入学号/用户名password加密计算DES(密钥2, 明文密码)croypto#login-croypto密钥2原值type#current-login-type固定值 UsernamePassword_eventId固定值submitexecution UUID_GzipBase64captcha_code用户输入验证码（可选）geolocation固定值空字符串7.2 加密时序1. 页面加载 ↓ 2. 提取 login-croypto → 密钥2 (zgEBWBZtq5s=) ↓ 3. 用户输入密码 → password (test123) ↓ 4. Base64解码密钥2 zgEBWBZtq5s= → [0xce, 0x01, 0x01, 0x58, 0x16, 0x56, 0xdb, 0x9b] ↓ 5. DES-ECB加密 plaintext: test123 (7字节) padding: test123\x01 (8字节, PKCS7) encrypted: [0x0a, 0xcb, 0xdc, 0xc5, 0x87, 0x42, 0xa8, 0xd4] ↓ 6. Base64编码 CsvcxYdCqNQ= ↓ 7. 提交表单7.3 设备检测流程从 loginNew.js 可以看到完整的设备检测逻辑：// 1. 创建检测元素 var newElement = document.createElement('div'); newElement.classList.add('pl_box'); newElement.id ='pl_dom'; document.body.appendChild(newElement); // 2. CSS媒体查询 // @media screen and (orientation: portrait) { // .pl_box { width: 20px; height: 30px; } // } // @media screen and (orientation: landscape) { // .pl_box { width: 30px; height: 20px; } // } // 3. 判断方向 const width = document.getElementById('pl_dom').clientWidth; const height = document. getElementById('pl_dom').clientHeight; if (parseInt(width) < parseInt(height)) { neworientation. current = 'portrait'; // 竖屏 } else { neworientation. current = 'landscape'; // 横屏 } // 4. 根据方向加载不同页面 if (landscape && res == 'new') { device = 'PC'; script. onload = function () { window.casPageInit("loginnew"); }; } else if (landscape) { device = 'PC'; script.onload = function () { window.casPageInit("login"); }; } else if (portrait) { device = 'PHONE'; script.onload = function () { window.casPageInit("login"); }; }7.4 自动重定向逻辑从 login 第203-229行：var list = document.getElementsByClassName('ruleList'); var isAdapterRedirect = false; var isAutoRedirect = false; var isAutoRedirectClientName = null; // 检查是否有自动重定向的认证方式 for (var i = 0; i < list.length; i++) { var ele = list[i]. getElementsByClassName('code')[0]; var code = ele.innerText; var autoRedirect = list[i].getElementsByClassName('isRedirect')[0]; var autoRedirectCode = autoRedirect.innerText; if (autoRedirectCode == 'true') { isAutoRedirect = true; isAutoRedirectClientName = code; break; } if (code == 'adapter') { isAdapterRedirect = true; } } // 执行重定向 if (isAutoRedirect) { url = url.replace('#client_name', isAutoRedirectClientName); window.location. href = url; } if (isAdapterRedirect) { url = url.replace('#client_name', 'adapter'); window.location.href = url; }7.5 配置请求流程从 deploy.js 第267-283行：getSelfConfig('URL_CRYPTO_DATA', function () { console.log(configUrl, config); getSelfConfig('pc. self.page.config', function () { console.log(configUrl, config); if (pageName === 'serviceerror') { // 处理服务错误页面 } else if (pageName === 'logout' || pageName === 'propagatelogout' || pageName === 'error') { // 处理登出页面 if (document.getElementById('logout-header-logo')) { document.getElementById('logout-header-logo').src = window.convertHref(config.logo); } if (document.getElementById('logout-body-bg')) { document.getElementById('logout-body-bg').style.backgroundImage = "url('" + window.convertHref(config.background) + "')"; } } else { // 处理登录页面 _casPageInit(); } }); });配置加载顺序：URL_CRYPTO_DATA - 获取需要加密的API列表pc.self.page.config - 获取页面自定义配置根据页面类型执行相应初始化7.6 错误处理从 login 第234-252行：var clientType = document.getElementById("current-login-type").innerText; var errorCode = document.getElementById("login-error-code").innerText; if (errorCode === "1410041" && (clientType == "dingDingWlan" || clientType == "adapter" || ... )) { if (clientType == "") { clientType = "appToken" } url = "./public/client/bind/fail?clientname=#clientname"; url = url.replace('#clientname', clientType); window.location.href = url; } if (errorCode === "1030028" && (clientType == "dingDingWlan" || clientType == "adapter" || ...)) { if (clientType == "") { clientType = "appToken" } url = "./public/client/bind/error?clientname=#clientname"; url = url.replace('#clientname', clientType); window.location.href = url; }错误码含义：1410041 - 绑定失败，跳转到 bind/fail 页面1030028 - 绑定错误，跳转到 bind/error 页面本报告的所有内容仅供学习和研究使用。作者通过分析公开可访问的前端代码和网络请求，学习和理解现代Web应用的认证机制,加密实现和前端架构设计。作者从未、也绝不会利用本内容进行任何非法活动。作者不对他人使用本报告内容产生的任何后果承担责任。任何人使用本报告内容从事违法活动，均与作者无关，由使用者自行承担全部法律责任。
九江学院统一身份认证系统登录加密逆向分析
九江学院统一身份认证系统登录加密逆向分析📋 目录一、背景介绍二、初步侦察三、关键文件分析四、加密流程深度解析五、总结一、背景介绍1.1 目标系统系统名称：九江学院统一身份认证平台登录地址：https://authserver.jju.edu.cn/authserver/login目标：分析并理解前端密码加密逻辑1.2 技术栈前端加密：CryptoJS (AES-CBC加密)后端框架：基于CAS (Central Authentication Service)传输协议：HTTPS1.3 分析工具Chrome DevTools (网络抓包、调试)HTML源码查看JavaScript代码分析二、初步侦察2.1 网络请求抓包打开登录页面，按 F12 进入开发者工具，切换到 Network 标签，执行一次登录操作：POST /authserver/login? service=https://ehall.jju.edu.cn/login HTTP/1.1 Host: authserver.jju.edu.cn Content-Type: application/x-www-form-urlencoded username=202012345& password=U2FsdGVkX1%2BabcdefghijklmnopqrstuvwxyzABCDEF... & execution=e7637a06-8937-4197-b207-5986a5a159e6_... & _eventId=submit& cllt=userNameLogin关键发现：✅ 用户名明文传输✅ 密码已加密（Base64格式）✅ 存在 execution 参数（类似CSRF Token）2.2 页面源码分析右键查看页面源代码，搜索关键字 password、encrypt、salt： 发现：AES加密密钥直接嵌入在HTML页面的隐藏字段中三、关键文件分析3.1 加密核心文件：encrypt.js定位到 /authserver/jjuThemea/static/common/encrypt.js3.1.1 随机字符串生成器// 定义随机字符集（排除易混淆字符 I, L, O, 0, 1, 9） var $aes_chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; var aes_chars_len = $aes_chars.length; // 生成指定长度的随机字符串 function randomString(len) { var retStr = ''; for (i = 0; i < len; i++) { retStr += $aes_chars.charAt(Math.floor(Math.random() * aes_chars_len)); } return retStr; }原理解析：字符集包含 48 个字符（26个大写字母 + 22个小写字母和数字）排除了 I, L, O, 0, 1, 9 等易混淆字符使用 Math.random() 生成伪随机数每次调用返回指定长度的随机字符串3.1.2 AES加密核心函数// AES加密核心函数 function getAesString(data, key0, iv0) { // 去除密钥首尾空字符 key0 = key0.replace(/(^\s+)|(\s+$)/g, ""); // 将密钥和IV转换为CryptoJS可识别的格式 var key = CryptoJS.enc. Utf8.parse(key0); var iv = CryptoJS.enc.Utf8.parse(iv0); // 执行AES加密 var encrypted = CryptoJS.AES.encrypt(data, key, { iv: iv, // 初始化向量 mode: CryptoJS.mode.CBC, // CBC模式 padding: CryptoJS.pad.Pkcs7 // PKCS7填充 }); // 返回Base64编码的密文 return encrypted.toString(); }原理解析：参数作用说明data明文数据要加密的内容key0加密密钥16字节AES密钥iv0初始化向量16字节随机IVmode: CBC加密模式密码块链接模式padding: Pkcs7填充方式PKCS#7填充标准3.1.3 完整加密流程// 密码加密入口函数 function encryptPassword(pwd0, key) { try { return encryptAES(pwd0, key); } catch (e) { return pwd0; } } // 完整加密实现 function encryptAES(data, aesKey) { if (!aesKey) { return data; } // 关键步骤： // 1. 生成64位随机前缀 // 2. 拼接：随机前缀 + 原始密码 // 3. 生成16位随机IV // 4. 调用AES加密 var encrypted = getAesString( randomString(64) + data, // 64位随机前缀 + 密码 aesKey, // 加密密钥 randomString(16) // 16位随机IV ); return encrypted; }加密流程可视化：原始密码: "MyPassword123" ↓ 生成64位随机前缀: "TxmRpDkNwYs... 64个字符..." ↓ 拼接明文: "TxmRpDkNwYs...64个字符... MyPassword123" ↓ 生成16位随机IV: "AbCdEfGhJkMnPqRs" ↓ AES-CBC加密 (使用密钥和IV) ↓ Base64编码 ↓ 最终密文: "U2FsdGVkX1+abc..."3.2 登录逻辑文件：login.js定位到 /authserver/jjuThemea/static/web/js/login.js3.2.1 密钥获取与加密调用// 表单验证与加密函数 function checkForm() { var cllt = $(". login-main #cllt").val(); if (cllt == 'userNameLogin') { // 验证用户名和密码... if (utils.requireInput($(LOGIN_USERNAME_ID), 0, 100, ... )) { return; } if (utils.requireInput($(LOGIN_PASSWORD_ID), 0, 32, ...)) { return; } // 从HTML页面获取AES密钥 var aesKey = $("#pwdEncryptSalt").val(); // 获取用户输入的密码 var password = $(LOGIN_PASSWORD_ID).val(); // 使用密钥加密密码 var encryptedPassword = encryptPassword(password, aesKey); // 将加密后的密码存入隐藏字段 $("#saltPassword").val(encryptedPassword); // 禁用原始密码输入框（防止明文提交） $(LOGIN_PASSWORD_ID).attr("disabled", "disabled"); } return true; }流程说明：用户点击登录按钮 ↓ 触发 checkForm() 函数 ↓ 验证用户名和密码格式 ↓ 从页面获取密钥: $("#pwdEncryptSalt").val() ↓ 获取用户输入: $(LOGIN_PASSWORD_ID).val() ↓ 调用加密函数: encryptPassword(password, aesKey) ↓ 将密文存入隐藏字段: $("#saltPassword").val(encrypted) ↓ 禁用原始密码输入框 ↓ 提交表单（只提交密文）3.3 HTML页面分析：login_page.html3.3.1 关键表单字段 登录 字段说明表：字段名ID/Name类型作用是否提交用户名usernametext用户输入✅ 明文提交原始密码passwordTextpassword用户输入❌ 被禁用加密密码password (saltPassword)hidden存储密文✅ 密文提交加密密钥pwdEncryptSalthidden存储密钥❌ 不提交验证码captchatext用户输入✅ 明文提交执行标识executionhiddenCSRF保护✅ 提交3.3.2 密钥生成机制服务器端（伪代码）： @GetMapping("/login") public String loginPage(HttpSession session, Model model) { // 1. 生成16位随机密钥 String aesKey = generateRandomKey(16); // 例如: "V4Kh77JL6yk52pNG" // 2. 存入服务器Session（用于后续解密） session.setAttribute("aesKey", aesKey); // 3. 将密钥传递给前端页面 model.addAttribute("pwdEncryptSalt", aesKey); return "login"; }密钥生命周期：用户访问登录页面 ↓ 服务器生成随机密钥（16位） ↓ 存入Session: session.put("aesKey", key) ↓ 嵌入HTML: ↓ 返回页面给浏览器 ↓ JavaScript读取密钥进行加密 ↓ 用户提交登录 ↓ 服务器从Session取出密钥 ↓ 使用相同密钥解密密码 ↓ 验证密码是否正确四、加密流程深度解析4.1 完整加密流程图┌─────────────────────────────────────────────────────┐ │ 步骤1: 用户输入密码 │ │ 输入: "MyPassword123" │ └────────────────┬────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────┐ │ 步骤2: JavaScript从HTML获取密钥 │ │ $("#pwdEncryptSalt").val() → "V4Kh77JL6yk52pNG" │ └────────────────┬────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────┐ │ 步骤3: 生成64位随机前缀 │ │ randomString(64) │ │ → "TxmRpDkNwYsFqJzH... 64个字符..." │ └────────────────┬────────────────────────────────────┘ ↓ ┌─────────────────���───────────────────────────────────┐ │ 步骤4: 拼接明文 │ │ "TxmRpDkNwYsF...64位... MyPassword123" │ │ 总长度: 64 + 13 = 77 字符 │ └────────────────┬────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────┐ │ 步骤5: 生成16位随机IV │ │ randomString(16) │ │ → "AbCdEfGhJkMnPqRs" │ └────────────────┬────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────┐ │ 步骤6: UTF-8编码 │ │ 明文 → 77字节 │ │ 密钥 → 16字节 │ │ IV → 16字节 │ └────────────────┬────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────┐ │ 步骤7: PKCS7填充 │ │ 77字节 → 填充到16的倍数 → 80字节 │ │ 填充内容: 添加3个字节 0x03 │ └────────────────┬────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────┐ │ 步骤8: AES-CBC加密 │ │ 明文块1(16B) ⊕ IV → AES → 密文块1 │ │ 明文块2(16B) ⊕ 密文块1 → AES → 密文块2 │ │ 明文块3(16B) ⊕ 密文块2 → AES → 密文块3 │ │ 明文块4(16B) ⊕ 密文块3 → AES → 密文块4 │ │ 明文块5(16B) ⊕ 密文块4 → AES → 密文块5 │ │ 结果: 80字节密文 │ └────────────────┬────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────┐ │ 步骤9: 打包IV和密文 │ │ [IV(16字节)][密文(80字节)] = 96字节 │ └────────────────┬────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────┐ │ 步骤10: Base64编码 │ │ 96字节 → Base64 → 128字符 │ │ "U2FsdGVkX1+abcdefghijk..." │ └────────────────┬────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────┐ │ 步骤11: 提交到服务器 │ │ POST /authserver/login │ │ password=U2FsdGVkX1+abc... │ └─────────────────────────────────────────────────────┘4.2 三大核心机制4.2.1 机制一：64位随机前缀作用：防止密码字典攻击// 实现代码 randomString(64) + password原理：每次加密都在密码前添加64位随机字符，使得相同密码产生完全不同的密文4.2.2 机制二：16位随机IV作用：确保相同明文产生不同密文// 实现代码 var iv = randomString(16); CryptoJS.AES.encrypt(data, key, { iv: iv, mode: CryptoJS.mode.CBC });原理：CBC模式下，IV参与第一个数据块的加密，每次使用不同的IV会产生不同的密文4.2.3 机制三：动态密钥作用：防止密钥重用和会话劫持 原理：服务器为每个会话生成独立的加密密钥，存储在Session中，客户端从HTML页面获取并使用4.3 数据结构4.3.1 加密前的明文结构┌──────────────────────────────────────┬─────────────────┐ │ 64位随机前缀 │ 原始密码 │ ├──────────────────────────────────────┼─────────────────┤ │ TxmRpDkNwYsFqJzHcBnMaXvKgWeRtPsJf │ MyPassword123 │ │ DhLmQwZxCvBnMkYuHgTrEwQpLkJh │ │ └──────────────────────────────────────┴─────────────────┘ 64 字符 13 字符 ↓ 总长度: 77 字符 ↓ UTF-8编码: 77 字节 ↓ PKCS7填充: 80 字节4.3.2 加密后的密文结构┌─────────────────┬──────────────────────────────────┐ │ IV (16字节) │ 实际密文 (80字节) │ ├─────────────────┼──────────────────────────────────┤ │ AbCdEfGhJkMn... │ [加密后的二进制数据] │ └─────────────────┴──────────────────────────────────┘ 16字节 80字节 ↓ 总计: 96字节 ↓ Base64编码: 128字符4.4 服务器端解密流程接收密文: "U2FsdGVkX1+abc..." ↓ Base64解码 → 96字节 ↓ 分离: IV(16字节) + 密文(80字节) ↓ 从Session获取密钥: "V4Kh77JL6yk52pNG" ↓ AES-CBC解密 ↓ 去除PKCS7填充 → 77字节 ↓ 去掉前64位随机前缀 ↓ 得到原始密码: "MyPassword123" ↓ 验证密码是否正确五、总结5.1 核心技术要点加密算法：算法: AES-128 模式: CBC (Cipher Block Chaining) 填充: PKCS7 编码: Base64三大核心机制：序号机制参数作用1随机前缀64位字符防止字典攻击2随机IV16字节增加密文随机性3动态密钥16字节防止密钥重用完整流程：前端流程: 1. 访问登录页面 → 获取动态密钥 2. 生成64位随机前缀 → 拼接密码 3. 生成16位随机IV → 执行AES加密 4. 打包IV和密文 → Base64编码 5. 提交密文到服务器 后端流程: 1. 接收Base64密文 → 解码 2. 分离IV和密文 → 从Session获取密钥 3. AES解密 → 去除填充 4. 去掉64位前缀 → 得到原始密码 5. 验证密码 → 返回结果5.2 关键知识点密钥管理：来源: HTML页面隐藏字段 长度: 16字节 (AES-128)生命周期: Session级别（约30分钟）获取方式: $("#pwdEncryptSalt").val()核心函数：randomString(length) // 生成随机字符串 getAesString(data, key, iv) // AES加密核心 encryptPassword(password, key) // 密码加密入口5.3 常见问题Q: 密钥从哪里来？A: 从HTML页面的隐藏字段 中获取Q: 每次登录密钥都不同吗？A: 是的，每次访问登录页面服务器会生成新的密钥Q: 为什么要加64位随机前缀？A: 防止密码字典攻击，增加破解难度到 10^108Q: IV的作用是什么？A: 确保相同密码产生不同密文，可以公开传输（编码在密文前16字节）5.4 免责声明本文档仅供学习和研究使用，请勿用于非法用途。 使用本文档内容造成的任何后果由使用者自行承担。 建议在合法合规的前提下学习相关技术。文档信息版本: v1.0类型: 逆向分析文档目的: 学习研究许可: 仅供学习使用
OpenList WebUI for OpenWrt - 一个独立的 OpenList (Alist分支) OpenWrt LuCI 管理界面
OpenList WebUI for OpenWrt - 一个独立的 OpenList (Alist分支) OpenWrt LuCI 管理界面 项目地址：drfccv/luci-app-openlistui 功能特性🎯 现代化界面设计 - 采用响应式布局，支持多种设备访问📊 系统概览 - 实时显示系统状态、服务运行情况和资源使用🔄 更新管理 - 便捷的组件更新和版本管理📝 日志查看 - 实时日志监控，支持过滤和自动刷新⚙️ 配置管理 - 简化的配置界面，支持各种参数调整🌐 多语言支持 - 支持中文界面界面预览系统概览系统概览页面提供了完整的系统状态监控，包括：服务运行状态系统资源使用情况快速操作按钮系统信息展示更新管理更新管理页面支持：组件版本查看一键更新功能更新进度显示更新日志查看配置设置配置设置页面包含：服务配置选项网络设置安全配置高级选项快速开始编译安装# 编译默认架构 (x86_64) ./build.sh # 编译指定架构 ./build.sh aarch64 ./build.sh mips ./build.sh mipsel ./build.sh arm ./build.sh armv7 # 编译所有支持的架构 ./build.sh all # 快速编译（跳过feeds更新） ./build.sh --fast ./build.sh -f x86_64 # 开发模式（包含时间戳） ./build.sh --dev x86_64 # 显示版本信息 ./build.sh version # 清理构建文件 ./build.sh clean # 显示帮助信息 ./build.sh help构建输出每次构建会生成：IPK包: output/luci-app-openlistui_架构_时间戳.ipk构建信息: output/luci-app-openlistui_架构_时间戳.info构建日志: build-logs/build-架构-时间戳.log版本信息: 包内 /usr/lib/lua/luci/version-openlistui配置文件可以通过 build.conf 自定义构建参数：默认架构SDK URLs构建选项依赖包列表支持的架构x86_64 - x86 64位 (默认)aarch64 - ARM 64位mips - MIPS 大端序 (ath79)mipsel - MIPS 小端序 (ramips)arm - ARM 32位 (bcm2708)armv7 - ARMv7 (bcm2709)构建选项-f, --fast - 跳过feeds更新 (适用于重复构建)--dev - 开发模式，为包文件名添加时间戳安装说明编译IPK包: 使用构建脚本编译对应架构的IPK包安装界面包: 安装编译生成的 luci-app-openlistui IPK包重启服务: 重启 uhttpd 服务以加载新界面访问界面: 通过 OpenWrt 管理界面访问 OpenList 管理页面开发说明目录结构luci-app-openlist/ ├── luasrc/ # Lua 源码 │ ├── controller/ # 控制器 │ ├── model/ # 数据模型 │ └── view/ # 视图模板 ├── htdocs/ # 静态资源 ├── po/ # 多语言文件 ├── root/ # 系统文件 ├── screenshots/ # 界面截图 └── CONTROL/ # 包控制文件开发命令# 检查版本信息 make version # 生成版本文件（调试用） make gen-version # 查看构建变量 grep "PKG_" Makefile注意事项此包为独立的 LuCI 网页界面，无需依赖额外的核心包使用 openlistui 命名空间避免与原始 luci-app-openlist 冲突确保 OpenWrt 系统版本兼容性支持多种主流路由器架构许可证MIT License贡献欢迎提交 Issue 和 Pull Request 来改进这个项目。OpenList WebUI
🚄 MCP Server 12306✨ 项目简介MCP Server 12306是一款基于 Model Context Protocol (MCP) 的高性能火车票查询后端，支持官方 12306 余票、票价、车站、经停、换乘查询以及智能时间工具，适配 AI/自动化/智能助手等场景。界面友好，易于集成，开箱即用。🚀 功能亮点实时余票/车次/座席/时刻/换乘一站式查询全国车站信息管理与模糊搜索官方经停站、一次中转方案全支持实时查询各车次票价信息智能时间工具，支持时区和时间戳Streamable HTTP/STDIO传输协议，支持MCP 2025-03-26标准FastAPI异步高性能，秒级响应MCP标准，AI/自动化场景即插即用🛠️ 快速上手本项目支持两种运行模式：Stdio 模式：适用于 Claude Desktop 等本地 MCP 客户端（推荐）。Streamable HTTP 模式：适用于远程部署或通过 SSE/Post 访问。模式 1：Stdio 模式（Claude Desktop 推荐）在此模式下，MCP Server 通过标准输入/输出与客户端通信，无需占用网络端口。方式 A：使用 uvx（推荐）uvx 是 uv 包管理器提供的工具，环境隔离且启动极快。{ "mcpServers": { "12306": { "command": "uvx", "args": ["mcp-server-12306"] } } }方式 B：使用 pipx如果您更习惯使用 pipx：{ "mcpServers": { "12306": { "command": "pipx", "args": ["run", "--no-cache", "mcp-server-12306"] } } }方式 C：本地源码运行适用于开发者调试：{ "mcpServers": { "12306": { "command": "uv", "args": ["run", "python", "-m", "mcp_12306.cli"], "cwd": "/path/to/mcp-server-12306" } } }模式 2：Streamable HTTP 模式在此模式下，Server 启动一个 Web 服务（默认 8000 端口），支持 MCP 的 SSE（Server-Sent Events）和 POST 交互。方式 A：本地源码运行# 1. 克隆并安装依赖 git clone https://github.com/drfccv/mcp-server-12306.git cd mcp-server-12306 uv sync # 2. 启动服务器 uv run python scripts/start_server.pyMCP 客户端配置：{ "mcpServers": { "12306": { "url": "http://localhost:8000/mcp" } } }方式 B：Docker 部署# 拉取镜像并运行 docker run -d -p 8000:8000 --name mcp-server-12306 drfccv/mcp-server-12306:latest🤖 工具一览支持的主流程工具工具名典型场景/功能描述query_tickets余票/车次/座席/时刻一站式查询query_ticket_price实时查询各车次票价信息search_stations车站模糊搜索，支持中文/拼音/简拼get_station_info获取车站详情（名称、代码、地理等）query_transfer一次中转换乘方案，自动拼接最优中转get_train_route_stations查询指定列车经停站及时刻表get_current_time获取当前时间与相对日期，帮助用户准确选择出行日期📚 工具文档本项目所有主流程工具的详细功能、实现与使用方法，均已收录于 /docs 目录下：query_tickets.md — 余票/车次/座席/时刻一站式查询query_ticket_price.md — 实时查询各车次票价信息search_stations.md — 车站智能搜索query_transfer.md — 一次中转换乘方案get_train_route_stations.md — 查询列车经停站get_current_time.md — 获取当前时间与相对日期每个文档包含：工具功能说明实现方法请求参数与返回示例典型调用方式如需二次开发或集成，建议先阅读对应工具的文档。🧩 目录结构src/mcp_12306/ # 主源代码 ├─ server.py # FastAPI主入口 ├─ services/ # 业务逻辑（车票/车站/HTTP） ├─ utils/ # 工具与配置 scripts/ # 启动与数据脚本📄 LicenseMIT License⚠️ 免责声明本项目仅供学习、研究与技术交流，严禁用于任何商业用途。本项目不存储、不篡改、不传播任何 12306 官方数据，仅作为官方公开接口的智能聚合与转发。使用本项目造成的任何后果（包括但不限于账号封禁、数据异常、法律风险等）均由使用者本人承担，项目作者不承担任何责任。请遵守中国法律法规及 12306 官方相关规定，合理合规使用。
正方教务系统爬虫-微信小程序前后端
正方教务系统完整解决方案 - 微信小程序 + 后端 API项目简介这是一个完整的正方教务系统第三方解决方案，包含微信小程序前端和 Python 后端 API 服务。该项目为学生提供便捷的教务管理服务，支持课表查询、成绩管理、选课系统等核心功能。⚠️ 重要说明：本项目为第三方开发的非官方 API，仅供学习交流使用。🔗 前端项目: ZFJW-miniprogram 🔗 后端项目: ZFJW-backend项目架构整体架构图┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ │ │ │ │ │ │ │ 微信小程序端 │◄──►│ Python 后端 API │◄──►│ 正方教务系统 │ │ │ │ │ │ │ │ - TypeScript 100% │ │ - Python 99.6% │ │ - 各高校教务系统 │ │ - 本地数据缓存 │ │ - Flask 框架 │ │ - 第三方接口调用 │ │ - 隐私保护设计 │ │ - RESTful API │ │ - 数据获取与解析 │ │ │ │ │ │ │ └─────────────────────┘ └─────────────────────┘ └─────────────────────┘技术栈对比组件前端小程序后端 API语言TypeScript (100%)Python (99.6%)框架微信小程序原生Flask 2.0+特性本地缓存、隐私保护RESTful API、多校支持部署微信小程序平台独立服务器/Docker核心功能🎯 完整功能矩阵功能模块前端实现后端支持状态用户认证✅ 登录界面、密码管理✅ 验证码登录、Session 管理已完成个人信息✅ 信息展示、资料管理✅ 学生信息查询已完成智能课表✅ 周视图、手势滑动✅ 课表查询已完成成绩管理✅ 多学期查询、GPA 计算✅ 成绩统计分析已完成考试安排✅ 考试时间展示✅ 考试信息查询已完成选课系统✅ 已选课程显示🚧 选课操作开发中部分完成教学评价✅ 一键评教功能✅ 评价数据处理已完成通知公告✅ 通知展示✅ 消息推送已完成空教室查询🚧 界面开发中🚧 查询接口开发中计划中🔥 核心特性前端特性🎨 现代化界面: 简洁美观的用户界面设计📱 响应式布局: 适配不同屏幕尺寸💾 智能缓存: 离线数据查看，减少网络请求🔒 隐私保护: 数据本地存储，不上传云端⚡ 流畅体验: 手势操作，快速响应后端特性🚀 高性能: 基于 Flask 的轻量级架构🏫 多校支持: 灵活的学校配置系统🔐 安全可靠: 完善的认证和会话管理🐳 容器化: 支持 Docker 部署📊 数据处理: 智能的数据解析和统计详细功能介绍📅 智能课表系统前端实现：// 课表周视图组件 interface ScheduleWeekView { currentWeek: number; scheduleData: CourseInfo[]; weekDays: string[]; timeSlots: TimeSlot[]; } // 手势滑动切换周次 const handleSwipeChange = (e: any) => { const { current } = e.detail; setCurrentWeek(current + 1); loadScheduleData(current + 1); };后端 API：@app.route('/api/schedule', methods=['GET']) def get_schedule(): """获取课表数据""" week = request.args.get('week', 1) try: schedule_data = zfn_api.get_schedule(week) return jsonify({ 'code': 200, 'data': schedule_data, 'message': 'success' }) except Exception as e: return jsonify({ 'code': 500, 'message': str(e) })📊 成绩管理系统前端功能：多学期成绩查询GPA 自动计算成绩统计图表学分统计展示后端处理：def calculate_gpa(grades_data): """计算 GPA""" total_credits = 0 total_grade_points = 0 for grade in grades_data: credit = float(grade.get('credit', 0)) score = float(grade.get('score', 0)) grade_point = score_to_gpa(score) total_credits += credit total_grade_points += credit * grade_point return total_grade_points / total_credits if total_credits > 0 else 0🔐 安全认证机制前端实现：// 登录管理器 class LoginManager { private static instance: LoginManager; // 登录验证 async login(username: string, password: string, captcha: string) { const loginData = { username, password: this.encryptPassword(password), captcha }; const response = await api.post('/login', loginData); if (response.code === 200) { this.saveLoginInfo(response.data); return true; } return false; } // 密码加密 private encryptPassword(password: string): string { // 实现密码加密逻辑 return btoa(password); } }后端验证：@app.route('/api/login', methods=['POST']) def login(): """用户登录验证""" data = request.get_json() username = data.get('username') password = data.get('password') captcha = data.get('captcha') try: # 验证登录信息 login_result = zfn_api.login(username, password, captcha) if login_result['success']: # 创建会话 session['user_id'] = username session['login_time'] = datetime.now() return jsonify({ 'code': 200, 'data': login_result['data'], 'message': '登录成功' }) else: return jsonify({ 'code': 401, 'message': login_result['message'] }) except Exception as e: return jsonify({ 'code': 500, 'message': f'登录失败: {str(e)}' })部署指南🖥️ 后端部署本地开发环境# 1. 克隆后端项目 git clone https://github.com/drfccv/ZFJW-backend.git cd ZFJW-backend # 2. 创建虚拟环境 python -m venv venv source venv/bin/activate # Linux/Mac venv\Scripts\activate # Windows # 3. 安装依赖 pip install -r requirements.txt # 4. 配置学校信息 cp school_config.example.json school_config.json # 编辑 school_config.json 添加你的学校配置 # 5. 启动服务 python app.pyDocker 部署# 1. 构建镜像 docker build -t zfjw-backend . # 2. 运行容器 docker run -d -p 5000:5000 --name zfjw-backend zfjw-backend # 3. 查看日志 docker logs -f zfjw-backend生产环境部署# 使用 Gunicorn 部署 pip install gunicorn gunicorn -w 4 -b 0.0.0.0:5000 app:app # 或使用 uWSGI pip install uwsgi uwsgi --http :5000 --module app:app --processes 4📱 前端部署开发环境# 1. 克隆前端项目 git clone https://github.com/drfccv/ZFJW-miniprogram.git cd ZFJW-miniprogram # 2. 安装依赖 npm install # 3. 配置后端 API 地址 # 修改 utils/config.ts 中的 API_BASE_URL # 4. 微信开发者工具 # 使用微信开发者工具打开项目目录 # 开启"不校验合法域名"选项生产环境# 1. 配置服务器域名 # 在微信小程序后台添加 request 合法域名 # https://your-api-domain.com # 2. 上传代码 # 使用微信开发者工具上传代码 # 提交审核并发布学校配置添加新学校支持获取学校教务系统信息# 访问学校教务系统，获取以下信息： # - 教务系统 URL # - 登录接口路径 # - 验证码接口 # - 页面编码格式创建学校配置文件{ "school_code": "your_school", "school_name": "你的学校名称", "base_url": "http://jwxt.your-school.edu.cn", "login_url": "/jsxsd/xk/LoginToXk", "captcha_url": "/jsxsd/verifycode.servlet", "encoding": "gbk", "timeout": 30, "headers": { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Accept-Encoding": "gzip, deflate", "Connection": "keep-alive", "Upgrade-Insecure-Requests": "1" } }测试配置# 创建测试脚本 import requests from school_config import load_school_config config = load_school_config('your_school') session = requests.Session() # 测试登录 response = session.post( config['base_url'] + config['login_url'], data={ 'userAccount': 'test_username', 'userPassword': 'test_password', 'RANDOMCODE': 'test_captcha' }, headers=config['headers'] ) print(f"登录测试结果: {response.status_code}")常见问题与解决方案❓ 登录相关问题Q: 登录失败怎么办？A: 检查以下几点： 1. 学号密码是否正确 2. 验证码是否正确输入 3. 学校教务系统是否正常运行 4. 网络连接是否稳定 5. 后端服务是否正常启动Q: 验证码识别不准确？A: 验证码识别问题： 1. 刷新验证码重新输入 2. 检查验证码图片是否清晰 3. 确认输入的验证码字符正确 4. 如果持续失败，可能需要手动输入🔧 技术问题Q: 后端 API 启动失败？# 检查 Python 版本 python --version # 需要 3.7+ # 检查依赖安装 pip list | grep Flask # 检查端口占用 netstat -an | grep 5000 lsof -i :5000 # Linux/Mac # 查看详细错误日志 python app.py --debugQ: 小程序无法连接后端？// 检查 API 地址配置 const API_BASE_URL = 'https://your-api-domain.com'; // 检查网络请求 wx.request({ url: API_BASE_URL + '/api/health', success: (res) => { console.log('API 连接正常:', res.data); }, fail: (err) => { console.error('API 连接失败:', err); } });Q: 数据不显示或显示错误？A: 数据问题排查： 1. 检查登录状态是否有效 2. 确认学期时间设置正确 3. 查看控制台错误日志 4. 验证后端 API 返回数据 5. 清除缓存重新获取数据安全与隐私🔒 数据安全措施前端安全敏感数据本地加密存储不上传个人信息到第三方服务器支持随时清除本地数据使用 HTTPS 加密通信后端安全不存储用户密码Session 超时自动清理请求频率限制输入数据验证和过滤通信安全API 接口使用 HTTPS数据传输加密防止中间人攻击会话令牌管理🛡️ 隐私保护承诺✅ 不收集用户个人隐私信息 ✅ 不进行用户行为跟踪分析 ✅ 数据仅用于教务查询功能 ✅ 支持用户随时删除数据 ✅ 遵循最小数据收集原则 ✅ 本地存储优先原则贡献指南🤝 参与贡献我们欢迎所有形式的贡献！贡献类型：🐛 Bug 修复✨ 新功能开发📝 文档完善🏫 学校配置添加🧪 测试用例编写🎨 UI/UX 改进贡献流程：Fork 项目到个人账号创建功能分支 git checkout -b feature/amazing-feature提交更改 git commit -m 'Add amazing feature'推送分支 git push origin feature/amazing-feature创建 Pull Request📋 开发规范代码规范：# 前端 TypeScript npm run lint # 代码检查 npm run format # 代码格式化 # 后端 Python pip install black flake8 black . # 代码格式化 flake8 . # 代码检查提交规范：feat: 新功能 fix: 修复bug docs: 文档更新 style: 代码格式调整 refactor: 重构代码 test: 测试相关 chore: 其他修改社区与支持💬 交流渠道GitHub Issues: 问题反馈GitHub Discussions: 讨论交流QQ 群: 2713587802（学习交流）📧 联系方式开发者: @drfccv邮箱: 27123587802@qq.com用途: 学习交流、问题反馈、功能建议免责声明⚠️ 重要提醒： 1. 本项目为第三方开发的非官方工具 2. 仅供学习交流和个人使用 3. 请勿用于商业用途或违法活动 4. 使用本项目造成的任何后果由使用者承担 5. 开发者不对数据准确性承担责任 6. 请遵守学校相关规定和法律法规开源协议本项目采用 MIT License 开源协议。致谢感谢所有贡献者和用户的支持！感谢微信小程序平台提供的开发环境感谢 Flask 框架的优秀设计感谢开源社区的无私奉献感谢所有提供学校配置的同学⭐ 如果这个项目对你有帮助，请给我们一个 Star！Made with ❤️ by drfccv | 最后更新: 2024-12-08
DeerFlow-CN本项目基于 bytedance/deer-flow 二次开发，专为中文用户优化，支持一键部署、SearXNG 集成、SSL 证书等。🌐 在线 Demo 体验项目简介DeerFlow-CN 是一款面向深度研究与高效信息检索的 AI 工具，结合大语言模型与多种外部工具，适用于学术、科研、知识管理等场景。相比原版，DeerFlow-CN 提供了更好的中文体验、屏幕适配与本地化增强。环境要求🟢 Node.js 22 及以上🐍 Python 3.12 及以上推荐工具⚡ uv：简化 Python 环境与依赖管理，自动创建虚拟环境并安装依赖，无需手动配置 Python 环境。🔄 nvm：高效管理多版本 Node.js 运行时。🚀 pnpm：高效安装和管理 Node.js 项目依赖。主要特点🇨🇳 全面汉化：界面、交互、设置、提示等全部中文化。📱 屏幕自适应：支持移动端、4K、竖屏等多种分辨率，体验更佳。💬 Chat 简单聊天模式：可选择不调用 researcher，仅用 LLM 聊天。🛠️ Settings 界面优化：修复 Tab 显示异常，交互更流畅。🔍 SearXNG 集成：支持 SearXNG 作为搜索来源，隐私友好。🧩 一键安装/启动 SearXNG：内置脚本自动安装并集成 SearXNG。⚡ 一键启动脚本：start.js/start-with-searxng.js 支持 SSL，证书自动加载。🔒 SSL 支持：可直接部署 HTTPS，证书与密钥位于 web/fullchain.pem、web/privkey.pem。项目架构├── main.py / server.py # 后端主入口 ├── src/ # 核心后端逻辑（agents/、llms/、tools/等） ├── web/ # 前端 Next.js + Tailwind + React │ ├── src/app/ # 页面与组件 │ ├── public/ # 静态资源 │ ├── start.js # 一键启动脚本（支持SSL） │ ├── start-with-searxng.js # 启动并集成SearXNG │ ├── fullchain.pem # SSL证书 │ ├── privkey.pem # SSL密钥 │ └── ... ├── conf.yaml # LLM与API配置 ├── .env # API密钥配置 ├── docs/ # 配置与使用文档 └── ...快速开始1. 克隆项目git clone https://github.com/drfccv/deer-flow-cn.git cd deer-flow-cn2. 安装依赖uv sync3. 配置环境变量复制并编辑 .env：cp .env.example .env # 配置 Tavily、Brave Search、volcengine TTS 等 API Key复制并编辑 conf.yaml：cp conf.yaml.example conf.yaml # 配置 LLM 接口与模型详细配置说明见 docs/configuration_guide.md4. 安装 Marp（PPT 生成）（可选）marp-cli 安装文档macOS: brew install marp-cli5. 安装 SearXNG（可选，推荐）bash ./src/tools/install-searxng.sh6. 安装前端依赖cd web pnpm install7. 一键启动（推荐，自动启用SSL）启动后端+前端+SearXNG（推荐）：node start-with-searxng.js仅启动后端+前端（不含SearXNG）：node start.js默认自动加载 web/fullchain.pem、web/privkey.pem 证书。8. 开发模式启动前端：pnpm dev启动后端：uv run server.py --ssl --ssl-certfile=web/fullchain.pem --ssl-keyfile=web/privkey.pem可省略 --ssl 参数则为 HTTP，添加 --ssl 参数即启用 HTTPS。主要配置说明.env：API 密钥（Tavily、Brave、TTS等）conf.yaml：LLM模型与API配置，详见 docs/configuration_guide.mdweb/fullchain.pem、web/privkey.pem：SSL证书与密钥主要改进与亮点全面汉化，适合中文用户响应式设计，适配多终端Chat支持“简单聊天”模式Settings界面Tab显示修复SearXNG一键集成与自动安装一键SSL部署，安全易用参考与致谢原项目：bytedance/deer-flowSearXNG: https://github.com/searxng/searxngMarp: https://github.com/marp-team/marp-cli如有问题请查阅 docs/FAQ.md 或提交 issue。
🦌 DeerFlow 项目分享一个由社区驱动的深度研究框架，结合语言模型与工具，如网页搜索、爬虫和 Python 执行，致力于回馈开源社区。(GitHub)📌 项目简介DeerFlow（Deep Exploration and Efficient Research Flow）是由字节跳动开源的多智能体研究自动化框架，旨在通过协调多个专用智能体（如搜索、编码、内容生成等）来增强复杂研究工作流程。该框架集成了 Python 执行、网页爬虫和 ByteDance 的 MCP 平台等工具，强调人机协作，使其在实际研究和企业应用中高度适应。 (Reddit)✨ 核心特性多智能体架构：每个智能体负责特定任务，如任务规划、知识检索、代码执行或报告合成，通过 LangGraph 构建的有向图进行交互，实现任务的稳健编排和数据流控制。 (Learnopoly)LangChain 与 LangGraph 集成：利用 LangChain 进行语言模型推理和记忆处理，LangGraph 实现多智能体的编排，支持复杂的研究流程。(Learnopoly)工具集成：网页搜索与爬虫：实时获取知识，聚合外部数据源。Python 执行与可视化：支持数据处理、统计分析和代码生成，提供执行验证。MCP 集成：兼容 ByteDance 的内部模型控制平台，支持更深入的自动化流程。多模态输出生成：不仅限于文本摘要，还可协同生成幻灯片、播客脚本或视觉内容。 (Learnopoly)人机协作：强调人类在研究流程中的参与，确保结果的透明性和可控性。(Learnopoly)🚀 快速开始环境要求Python：版本 3.12+Node.js：版本 22+(GitHub)推荐工具uv：简化 Python 环境和依赖管理。nvm：轻松管理多个 Node.js 运行时版本。pnpm：安装和管理 Node.js 项目的依赖项。(GitHub)安装步骤# 克隆仓库 git clone https://github.com/bytedance/deer-flow.git cd deer-flow # 安装依赖，uv 将自动创建虚拟环境并安装所需的包 uv sync # 配置 .env 文件，添加您的 API 密钥 cp .env.example .env请根据需要配置 .env 文件中的 API 密钥，例如 Tavily、Brave\_SEARCH 和 volcengine TTS。(GitHub)🧠 示例演示DeerFlow 提供了多个演示示例，展示了其在不同场景下的应用：埃菲尔铁塔与世界最高建筑的高度对比GitHub 上的热门趋势仓库撰写关于南京传统菜肴的文章如何装饰租赁公寓更多示例请访问官方项目页面：deerflow.tech(Reddit)🤝 鸣谢DeerFlow 构建在开源社区的卓越工作之上，特别感谢以下项目：(GitHub)LangChain：提供了强大的语言模型交互和链式调用框架。LangGraph：实现了多智能体的编排，支持复杂的工作流程。(GitHub, Learnopoly)特别感谢核心作者：(GitHub)Daniel WalnutHenry Li他们的愿景、热情和奉献使 DeerFlow 项目得以实现。📈 Star 趋势🖼️ 运行截图以下是我在本地运行 DeerFlow 的截图：如需了解更多信息，请访问项目的 GitHub 页面：https://github.com/bytedance/deer-flow
AI 旅行规划助手 🌍✈️一个基于人工智能的旅行规划助手，利用大语言模型和高德地图API为用户定制个性化的旅行计划。🔗 项目地址📝 在线体验 Demo✨ 功能特点🤖 AI驱动的旅行规划：基于硅基流动Qwen/Qwen3-235B-A22B模型智能生成详细的多日旅程🗺️ 高德地图集成：准确显示景点位置和路线规划🌦️ 实时天气信息：获取目的地的准确天气预报🏨 完整旅行建议：包含住宿、交通、餐饮和景点游览时间推荐💾 智能缓存机制：减少API调用，提高响应速度📱 响应式设计：适配各种设备尺寸，提供良好的移动端体验🚀 快速开始前提条件Node.js 16.x 或更高版本高德地图API密钥大语言模型API密钥 (如DeepSeek API)安装步骤克隆仓库git clone https://github.com/Drfccv/AI-Trip-Planner.git cd AI-Trip-Planner安装依赖npm install创建环境配置在项目根目录创建.env文件，添加以下内容：export const ENV = { AMAP_API_KEY: 'YOUR_AMAP_KEY', AMAP_SECURITY_KEY: 'YOUR_AMAP_SECURITY_KEY', DEEPSEEK_API_KEY: 'YOUR_SILICONFLOW_KEY' };启动开发服务器npm run dev打开浏览器访问 http://localhost:5173🏗️ 技术栈前端框架: React + TypeScript构建工具: ViteUI组件库: Ant Design地图服务: 高德地图 JavaScript API状态管理: React HooksHTTP客户端: AxiosAI服务: 硅基流动API (Qwen/Qwen3-235B-A22B模型)📝 使用指南在首页填写旅行信息表单:目的地城市旅行日期和天数交通方式偏好住宿偏好旅行风格标签点击"生成旅行计划"按钮系统将调用AI生成详细的旅行计划并展示:每日行程安排景点信息与推荐游览时间交通与住宿建议餐饮推荐天气预报互动地图可以随时返回调整参数重新规划🧩 项目结构src/ ├── assets/ # 静态资源 ├── components/ # 可复用组件 │ ├── AmapComponent.tsx # 高德地图组件 │ ├── LoadingPage.tsx # 加载页面组件 │ ├── TripInputForm.tsx # 旅行输入表单 │ └── TripPlanResult.tsx # 旅行计划结果展示 ├── pages/ # 页面组件 │ ├── HomePage.tsx # 首页 │ └── ResultPage.tsx # 结果页 ├── services/ # API服务 │ └── api.ts # API调用函数 ├── types/ # TypeScript类型定义 │ └── index.ts # 类型定义文件 └── utils/ # 工具函数 └── mockData.ts # 模拟数据🌟 核心功能实现细节AI旅行计划生成使用硅基流动提供的Qwen/Qwen3-235B-A22B大语言模型生成定制化的旅行计划。该模型是基于通义千问系列的超大规模语言模型，拥有2350亿参数量，具有强大的自然语言理解和生成能力。系统会将用户的旅行偏好（目的地、日期、天数、交通方式、住宿偏好和旅行风格标签）格式化后发送给模型，模型会返回结构化的旅行计划JSON数据，包括：每日详细行程安排景点推荐与描述游览时间建议餐饮推荐交通与住宿安排根据用户偏好定制的总体建议模型会基于城市特点、季节因素、用户偏好等多维度信息优化生成结果，并且会考虑景点之间的地理位置关系，合理安排游览顺序。高德地图集成景点定位: 通过高德POI搜索API获取景点准确位置路线规划: 根据交通方式提供合理的游览路线地图可视化: 直观展示每日或全程的景点分布(BUG)天气信息调用高德天气API获取目的地的实时天气预报，帮助用户做好行前准备。高德JSAPI仅可查询未来3天天气，后续考虑更换API服务📄 许可证MIT🤝 贡献指南欢迎提交Pull Request或Issue！对于重大变更，请先开Issue讨论您想要改变的内容。📞 联系方式如有任何问题或建议，请通过以下方式联系我：Email: 2713587802@qq.comGitHub: DrfccvAI旅行规划助手 - 让旅行计划变得简单而智能，由Qwen/Qwen3-235B-A22B模型提供强大的AI能力支持 🌈