当前位置: 首页 > news >正文

机票+火车票聚合查询与预订系统

核心需求与技术拆解

在动手前,把整个系统劈成了两条线:

  • 明线(业务层):在一个页面内无缝切换“机票/火车票”,处理单程/往返逻辑、多坐席选择、以及不同接口字段的UI抹平。
  • 暗线(数据层):抓包发现,真实接口的Header里藏了三个动态参数:token、transactionid和 sign。拿不到这三个大爷,浏览器连根毛都请求不到。

    针对这堆问题,我设计了四个核心模块。下面咱们挨个解剖。

    模块一:基于原生JS的“纯手工状态机”
    解决痛点:无框架下的跨业务线状态污染
  • 用原生JS写复杂交互,最怕的就是写出一坨”面条代码”(DOM操作和数据逻辑搅在一起)。我的解法是手动捍一个全局state对象,强制约束:所有点击事件只准修改数据,不准碰DOM;修改完后统一调用render()重绘。
  • 但这里有个大坑:当用户在查机票时,突然点了一下上方的“火车票”Tab,如果状态清理不干净,下方的列表就会出现“火车票列表里混着飞机航班号”的严重事故。

    源码剖析(前端 UI状态控制):
    // 全局唯一的数据源 (SSOT) const state = { activeTab: 'flight', // 当前业务线 flightList: [], // 承载搜索结果(机票/火车票复用) flightStep: 'depart' // 往返逻辑状态:去程/返程 // ... }; // 状态修改动作:严防数据污染 window.switchTab = (tab) => { state.activeTab = tab; state.hasSearched = false; // 核心点:切业务线时,必须强制清空上一业务的脏数据 state.flightList = []; state.apiError = null; // 智能容错:切火车票自动填高铁站,切机票自动填城市 if (tab === 'train') { state.fromCity = '北京南'; state.toCity = '上海虹桥'; } else { state.fromCity = '北京'; state.toCity = '上海'; } render(); // 触发全量重绘 };

    这种手工状态机的精髓在于对数据的绝对控制。哪怕是选坐席(二等座/头等舱),我也把具体价格动态绑到了 state.selectedDepTicket
    里,保证往返票在最后结算时,金额绝对不会算错。

    模块二:BFF代理与协议组装模块
    解决痛点:CORS跨域死锁与加密入参组装

  • 如果我们直接在前端用fetch请求某程的接口,浏览器直接就会爆红(CORS跨域拦截)。而且,那些签名字段放在前端太容易被扒了

  • 所以,我引入了Node四.js作为BFF层(前端专属后端)。前端只管传出发地、目的地,Node.js负责去拼装transactionld等校验参数。

    源码剖析(Python/Node 协议组装):

    # main.py (业务组装层片段) def getFlightInfo(departureCity, arrivalCity, departureDate): # 1. 预请求获取交易ID与核心配置参数 json_data = getTransactionId(departureCity['code'], arrivalCity['code'], departureDate) # 2. 从返回的 json_data 中提取 transactionID transactionId = json_data["transactionID"] # 3. 拼装原生字符串并 MD5 加密生成 sign signSrc = transactionId + departureCityCode + arrivalCityCode + departureDate sign = hashlib.md5(signSrc.encode('utf-8')).hexdigest() # 4. 调用 JS 虚拟机获取变态级别的风控 token token = ctx.call("getToken", json.dumps(json_data)) # 5. 带上这三兄弟,正式发起 batchSearch 请求 flights = getFly(sign, token, transactionId, json_data)

    复盘:在这个流程里,transactionlD算是白给的,在初始化页面的JSON 或者预检请求里就能截获;sign只是简单的参数拼接+MD5。真正的大头,是那个靠JS混淆生成的token

    模块三:JS虚拟环境补全(风控对抗核心)
    解决痛点:绕过浏览器指纹与高度混淆的

  • 点开生成token的源文件,我直接好家伙:一整个几万行的自执行函数,变量名全是 unknown xxx,里面充斥着类似RozanHyakuryuHa(庐山升龙霸)这种恶搞函数名,实际上底层走的是控制流平坦化(AST混淆)。

  • 强行解混淆太耗时间,最好的办法是黑盒调用(RPC/补环境)。大厂的风控脚本一定会疯狂检测执行环境是否是真实浏览器。为此,我搞了一个巨大的env.js,硬生生在Node环境里伪造了一个浏览器。

    源码剖析(env.js 补环境片段):

    // env.js (Node.js 补环境伪装浏览器) var v_new_toggle = true Object.freeze(console) // 阻断风控脚本检测控制台状态 // 伪造极其逼真的 Navigator 指纹 Navigator = v_saf(function Navigator(){ this._plugins = typeof PluginArray=='undefined'?[]:v_new(PluginArray); }) Object.defineProperties(Navigator.prototype, { userAgent: {get(){ return "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/131.0.0.0" }}, platform: {get(){ return "Win32" }}, webdriver: {get(){ return false }}, // 重点:反爬检测的核心指标 hardwareConcurrency: {get(){ return 32 }}, }) // 连底层的 WebGL 显卡渲染指纹都必须造假 WebGLRenderingContext = v_saf(function WebGLRenderingContext(){ this.getParameter = function(key){ if (this._toggle[key]){ // 伪装 NVIDIA 显卡信息 if (key == 37445){ return "Google Inc. (NVIDIA)" } if (key == 37446){ return "ANGLE (NVIDIA, NVIDIA GeForce GTX 1050 Ti Direct3D11 vs_5_0)" } } } })

    复盘:风控不仅查 window、document,连PerformanceTiming(页面加载性能耗时)、Crypto.getRandomValues (硬件随机数)、甚至Canvas渲染出来的toDataURL()base64图像特征都在查。环境补得越像,生成的token存活率就越高。

    模块四:沙箱执行与内存隔离
    解决痛点:加密脚本导致Node.js内存泄漏

  • 在服务端高频调用混淆JS时,最容易翻车的地方就是全局变量污染。那段“庐山升龙霸”代码会在window上挂载一堆加密状态,如果多次请求复用同一个JS上下文,很快就会报错或被风控封锁。

    源码剖析(sdt.js沙箱运行):

    // sdt.js const fs = require('fs'); const vm = require('vm'); // 1. 创建一个干净的全局上下文隔离区 const context = vm.createContext(global); // 2. 将补好的环境 (env.js) 丢进沙箱执行,防止污染宿主 Node 进程 vm.runInThisContext(fs.readFileSync('./env.js')); // 3. 混淆代码执行,向沙箱的 window 对象中注入 signature 函数 (function() { function RozanHyakuryuHa() { // ... (几万行控制流平坦化混淆代码) var _unknown_a6536 = decode(_unknown_c9301.b).split('').reduce(...) } try { RozanHyakuryuHa()(window, {"b": "PwEEAQUw...", "d": [...]}); } catch(e){} })(); // 4. 暴露出干净的调用入口 function getToken(param){ // 此时的 window 是 env.js 伪造的,signature 是混淆脚本挂载的 return window.signature(param) }

    复盘:利用Node自带的vm模块,我们让这坨来路不明的混淆代码跑在了一个受限的沙箱里。拿完token就走人,既保证了并发时的线
    程安全,也防住了内存泄漏。

    踩坑最多的地方
    除了逆向环境,前端业务其实有个极大的坑:数据结构聚合适配。
    机票接口查回来的对象叫 deptAirportName,坐席叫 cabin;但火车票查回来叫dept_ station_name,坐席是一个嵌套数组 cabin_info。
    为了不写两套重复的UI渲染代码,我在请求拿到数据后,立刻做了一层适配器模式(Adapter)。无论是飞机还是火车,统统洗成包含idtrainNo/fightNo,depTime,seats的标准对象。最后在订单结算页,只用一段模板字符串结合短路求值info.depAirportllinfo.depStation,就完美兼容了双端展示。
    总结
    这次不依赖ReactVue强行手撸聚合平台,加上跟大厂风控硬碰硬的逆向对抗,整个过程虽然蛋疼,但极其锻炼内功。当你不再是个纯粹的"APl调用侠,而是被迫去思考:浏览器指纹是如何生成的?沙箱内存该如何隔离?没有虚拟DOM状态流转该怎么闭环?你对整个Web生态的理解就会发生质的变化。
    希望这次的复盘,能给你在做架构设计或对抗风控时,提供一点不一样的思路。

http://www.jsqmd.com/news/453592/

相关文章:

  • 别感谢我,这是你应得的
  • 大数据开源工具大全:从ETL到BI的全套解决方案
  • 线程、进程、协程区别总结
  • LiuJuan20260223Zimage网站内容分析与SEO优化建议生成
  • 某教育企业智能合规平台架构实践:用AI满足教育行业合规
  • Ubuntu下丝滑地安装OpenClaw
  • Z-Image-GGUF算力优化:KSampler参数调优使单卡吞吐量提升2.3倍
  • SenseVoice-Small入门实战:快速构建个人语音笔记应用
  • 基于Reactor模式的简易HTTP服务端学习报告
  • 机顶盒设置密码/设置操作码/恢复出厂设置密码 2026年最新汇总分享
  • SUPER COLORIZER效果对比专题:不同参数下的色彩饱和度与风格差异研究
  • Lightroom 11.2.2 | Adobe出品,摄影师版PS,P图神器
  • 万物识别-中文镜像行业落地:林业遥感图像树种识别+林龄区间预测辅助
  • 2026年智能爬虫天花板:LLM+Python实现非结构化数据一键结构化提取
  • Spring_couplet_generation 环境隔离:Anaconda虚拟环境创建与管理
  • 光伏功率预测创新模型!基于非线性二次分解Ridge-RF-LSBoost时间序列预测MATLAB代码
  • 多任务学习:一鱼多吃
  • 数据结构优化:提升Qwen3-TTS语音特征处理效率
  • 自动驾驶3D目标检测:星图AI平台训练PETRV2-BEV模型教程
  • nlp_structbert_sentence-similarity_chinese-large实战:Java微服务中的语义查重与去重
  • 纯js表格编辑器已开源
  • 计算机组成原理 —— 计算机系统概述
  • 虚拟化中断传递的演进
  • 定制专属AI智脑:数谷智能助力企业沉淀核心数据价值
  • 墨语灵犀保姆级教程:解决‘砚池无响应’‘印章不显示’等6类常见问题
  • 使用CasRel进行软件测试报告分析:自动关联缺陷与代码模块
  • 信奥赛C++提高组csp-s之快速幂(案例实践1)
  • SmolVLA实战教程:Python调用app.py接口实现批量动作推理自动化
  • P1017 [NOIP 2000 提高组] 进制转换
  • css学习笔记