某公开数据简单逆向

政府网站 API 逆向分析实战:噪声数据采集

一句话总结

从目标网站前端 JS 中提取硬编码的第三方 API 地址和认证凭据,直接调用外部数据平台接口获取结构化数据。


逆向流程

步骤 1:访问目标网站

页面正常返回 200,HTML 中包含多个 <script> 标签。

步骤 2:定位核心 JS 文件

在 HTML 中找到关键脚本引用:

html 复制代码
<script src="../../images/zsjs*_sthjt_jkjs.js"></script>

步骤 3:下载并分析 JS

从目标网站下载对应的 JS 文件,发现是未混淆的明文代码

关键发现 1:API 配置(硬编码)

javascript 复制代码
var API_CONFIG = {
  baseUrl: "https://*.jlzslw.com:50003",   // 第三方数据平台
  username: "InterfaceCall",
  secretKey: "******",                      // 硬编码密钥
  sysCode: "NOISE"
};

关键发现 2:3 个 API 接口

搜索 JS 中的 ajax / Get 关键词,找到 3 个接口路径:

接口路径 功能
/GetExternalApiToken 获取 Token
/GetBSDStationListAsync 获取站点列表
/GetDATStationHourDisplayListAsync 获取噪声小时数据

步骤 4:分析 getToken 函数

javascript 复制代码
function getToken() {
  $.ajax({
    type: "GET",
    url: API_CONFIG.baseUrl + "/api/noiseproduct/AirCityBaseCommon/GetExternalApiToken",
    data: { UserName: API_CONFIG.username, SecretKey: API_CONFIG.secretKey },
    dataType: "json",
    success: function (response) {
      if (response && response.result) {
        RESULT_DATA.token = response.result;

关键点

  • 请求方式:GET
  • 参数名大小写敏感:UserNameSecretKey(首字母大写)
  • Token 位置:response.result

步骤 5:分析站点列表接口

javascript 复制代码
$.ajax({
  type: "GET",
  url: API_CONFIG.baseUrl + "/api/noiseproduct/AirCityProductBase/GetBSDStationListAsync",
  headers: { SysCode: API_CONFIG.sysCode, Authorization: "Bearer " + RESULT_DATA.token },

关键点

  • 请求方式:GET
  • 必须携带 Headers:SysCodeAuthorization: Bearer {token}

步骤 6:分析噪声数据接口

javascript 复制代码
$.ajax({
  type: "GET",
  url: API_CONFIG.baseUrl + "/api/noiseproduct/airdata/DATStationHour/GetDATStationHourDisplayListAsync",
  headers: { SysCode: API_CONFIG.sysCode, Authorization: "Bearer " + RESULT_DATA.token },
  data: $.param({
    codes: cityData.map(function (item) { return item.stationCode; }),
    dataType: 0,
    timePoint: dayjs().format("YYYY-MM-DD HH:00:00")
  }, true),

关键点

  • codes:站点编码列表
  • dataType: 0:固定值
  • timePoint必需字段 ,格式 YYYY-MM-DD HH:00:00

步骤 7:Python 验证

python 复制代码
import requests

session = requests.Session()
session.verify = False

# 1. 获取 Token
token = session.get(
    API_BASE + "/api/noiseproduct/AirCityBaseCommon/GetExternalApiToken",
    params={"UserName": "InterfaceCall", "SecretKey": "******"}
).json()["result"]

# 2. 获取站点列表
stations = session.get(
    API_BASE + "/api/noiseproduct/AirCityProductBase/GetBSDStationListAsync",
    headers={"SysCode": "NOISE", "Authorization": f"Bearer {token}"}
).json()["result"]

# 3. 获取噪声数据
data = session.get(
    API_BASE + "/api/noiseproduct/airdata/DATStationHour/GetDATStationHourDisplayListAsync",
    headers={"SysCode": "NOISE", "Authorization": f"Bearer {token}"},
    params={"codes": "1001A", "dataType": 0, "timePoint": "2026-05-22 12:00:00"}
).json()["result"]

数据流转图

复制代码
─────────────────────────────────────────────────────────────┐
│  1. GET /GetExternalApiToken                                │
│     → Token: "a24dbae..."                                  │
─────────────────────────────────────────────────────────────┤
│  2. GET /GetBSDStationListAsync                             │
│     Headers: SysCode=NOISE, Authorization=Bearer {token}    │
│     → 154 个站点 [{stationCode:"1001A", ...}, ...]         │
├─────────────────────────────────────────────────────────────┤
│  3. GET /GetDATStationHourDisplayListAsync                  │
│     ?codes=1001A&dataType=0&timePoint=2026-05-22 12:00:00  │
│     → 噪声数据 [{code:"1001A", leq:"48.4", ...}]           │
└─────────────────────────────────────────────────────────────

API 接口详情

1. 获取 Token

项目
URL https://*.jlzslw.com:50003/api/noiseproduct/AirCityBaseCommon/GetExternalApiToken
方法 GET
参数 UserName={硬编码用户名}&SecretKey={硬编码密钥}
返回 {"success":true, "result":"a24dbae..."}

2. 获取站点列表

项目
URL https://*.jlzslw.com:50003/api/noiseproduct/AirCityProductBase/GetBSDStationListAsync
方法 GET
Headers SysCode: NOISE Authorization: Bearer {token}
返回 {"result": [{id, positionName, stationCode, cityName, ...}]}

3. 获取噪声数据

项目
URL https://*.jlzslw.com:50003/api/noiseproduct/airdata/DATStationHour/GetDATStationHourDisplayListAsync
方法 GET
Headers 同上
参数 codes={站点编码}&dataType=0&timePoint={YYYY-MM-DD HH:00:00}
返回 {"result": [{code, name, leq, l5, l10, ...}]}

噪声数据字段说明

字段 说明 示例
code 站点编码 1001A
name 站点名称 某某小区
timePoint 监测时间 2026-05-22T12:00:00
leq 等效声级 dB(A) 48.4
l5 L5 52.9
l10 L10 51.4
l50 L50 46.1
l90 L90 42.3
lMax 最大声级 64.1
temperature 温度 ℃ 25.4
humidity 湿度 % 43
windSpeed 风速 m/s 2.3
isOverStandard 是否超标 false

与高难度站点的对比

对比项 高难度站点 本案站点
逆向难度 极难(VM 字节码混淆) 简单(JS 明文)
Token 来源 动态生成(解密函数) 固定凭据调用接口
API 地址 自有系统 第三方数据平台
数据托管 自有 托管至第三方平台

认证凭据(已脱敏)

复制代码
baseUrl:    https://*.jlzslw.com:50003
username:   InterfaceCall
secretKey:  ******
sysCode:    NOISE

注意:这些凭据硬编码在前端 JS 中,无需注册即可使用。


免责声明

本文仅用于技术学习和研究目的,展示的是如何通过前端逆向分析理解数据接口的工作原理。实际使用时请遵守相关法律法规和网站服务条款。

相关推荐
小江的记录本5 小时前
【Java并发编程】锁机制:volatile:JMM内存模型、可见性/禁止指令重排、内存屏障、单例模式中的应用(附《思维导图》+《面试高频考点清单》)
java·后端·python·mysql·单例模式·面试·职场和发展
csdn小瓯5 小时前
前端工程化:React + TypeScript + Tailwind CSS 的组件化实践
开发语言·人工智能·python
hef2885 小时前
R包grafify:简单操作实现高效统计绘图
开发语言·python·r语言
这是谁的博客?5 小时前
Python 异步编程核心原理与实践深度解析
java·网络·python·协程·asyncio·异步编程
战南诚5 小时前
力扣 之 198.打家劫舍
python·算法·leetcode
晚烛5 小时前
CANN 模型预热:消除首次推理延迟
开发语言·python
Fanxt_Ja6 小时前
Langchain+Tavily对接大模型实现联网搜索
python·langchain·deepseek·tavily
一个心烑6 小时前
【layui页面编辑下拉框处理的三种方式】
linux·python·layui