前情提要

最近在做一个数据包可视化的项目,需要知道所有经过服务器网关的数据包的信息,于是就有了这篇文章

项目地址:ShiDai / NetScope · GitLab

如果只是临时写个脚本,直接 requests.post() 当然也能用。但一旦你需要:

  • 反复登录爱快
  • 复用 sess_key
  • /Action/call
  • 获取终端列表
  • 获取每个终端的连接详询
  • 把这套能力接进 Django、Flask 或定时任务

那把它整理成一个小而清晰的 SDK,会省掉后面很多重复劳动。

这篇文章就结合一个实际可用的 ikuai_sdk,聊聊怎么把爱快接口封装成一个轻量、可复用的 Python SDK。

一、这个 SDK 解决了什么问题

这个 SDK 主要做了 4 件事:

  1. 封装爱快登录接口 /Action/login
  2. 自动提取 sess_key 并生成后续请求可复用的 cookie_header
  3. 封装通用 /Action/call
  4. 提供两个高频能力:
    • 获取终端列表
    • 获取单个终端的连接详询

对应的目录结构大致是这样:

sdk/
├── README.md
├── demo.py
├── .env.example
└── ikuai_sdk/
    ├── __init__.py
    ├── client.py
    ├── exceptions.py
    └── models.py

它的定位不是“大而全的爱快 Python 客户端”,而是“把最常用的几步先做好”。

二、爱快登录接口的特点

爱快登录接口是:

POST /Action/login

常见请求参数包括:

  • username
  • passwd
  • pass
  • remember_password

这里有两个细节比较关键:

1. 密码不是直接传明文

爱快要求传的是密码的 MD5 值,而且是 32 位小写。

也就是说,用户输入:

123

真正提交的是:

202cb962ac59075b964b07152d234b70

2. 登录成功后要拿 sess_key

爱快会在响应头里通过 Set-Cookie 返回 sess_key,后续很多接口都依赖这个值。

所以 SDK 不应该只关心登录 JSON 响应,还必须处理 Cookie。

三、SDK 的核心设计

这个 SDK 的核心入口是 IKuaiClient

最常用的调用方式是:

from ikuai_sdk import IKuaiClient

client = IKuaiClient()
result = client.login(
    router_url="http://10.1.1.1",
    username="admin",
    password="123",
)

print(result.result_code)
print(result.sess_key)
print(result.cookie_header)

这里有几个设计点值得说一下。

1. 对返回结果做结构化封装

SDK 没有直接返回原始字典,而是返回 IKuaiLoginResultIKuaiCallResult

这样做的好处是:

  • 字段更明确
  • 上层代码更好写
  • IDE 补全更友好
  • 不用每次自己手动 dict.get()

比如登录结果里会直接带这些字段:

  • router_url
  • login_url
  • username
  • request_mode
  • request_payload
  • upstream_status
  • upstream_response
  • cookies
  • sess_key
  • cookie_header

而且还额外提供了便捷属性:

  • result_code
  • result_message

这样业务层几乎不用自己再解析响应格式。

2. 自动兼容 JSON 和表单两种登录方式

爱快不同版本、不同环境下,有时对请求格式会比较敏感。

所以 SDK 登录时做了一个很实用的兼容策略:

  1. 先用 JSON 提交
  2. 如果没有拿到明确登录结果,再回退成表单方式提交

这能减少不少“明明接口对,为什么还是不通”的问题。

登录成功后,SDK 会自动拼出类似这样的 Cookie 头:

sess_key=0249f5edebd84e26103c1193a4ede2c8; username=admin; login=1

这样你后面调 /Action/call 时,不需要自己再拼接。

四、通用 /Action/call 为什么要封装

很多爱快能力,本质上都走的是:

POST /Action/call

所以 SDK 里单独做了一个通用方法:

call_result = client.call(
    router_url="http://10.1.1.1",
    cookie_header=login_result.cookie_header,
    payload={
        "func_name": "xxx",
        "action": "show",
        "param": {}
    },
)

这个设计很重要,因为它让 SDK 具备扩展性。

今天你只需要终端列表和连接详询,明天可能还要:

  • DHCP 信息
  • DNS 记录
  • 在线用户信息
  • 流量统计
  • 规则查询

只要底层 /Action/call 已经打通,上层新增能力就会很轻。

五、两个高频能力:终端列表与连接详询

1. 获取终端列表

SDK 内置了 get_terminal_list()

底层请求大致是:

{
  "func_name": "monitor_lanip",
  "action": "show",
  "param": {
    "TYPE": "data,total",
    "ORDER_BY": "ip_addr_int",
    "orderType": "IP",
    "limit": "0,100",
    "ORDER": ""
  }
}

调用方式:

terminal_result = client.get_terminal_list(
    router_url="http://10.1.1.1",
    cookie_header=login_result.cookie_header,
)

print(terminal_result.data.get("total"))
print(terminal_result.data.get("data"))

它适合做:

  • 当前在线终端查询
  • 局域网设备资产扫描
  • 自动巡检前的数据收集

2. 获取单设备连接详询

很多场景真正需要的不是“在线了多少设备”,而是“某台设备到底在连什么”。

这部分 SDK 已经封装成:

connection_result = client.get_terminal_connection_details(
    router_url="http://10.1.1.1",
    cookie_header=login_result.cookie_header,
    ip="10.0.1.2",
)

对应的请求体大致是:

{
  "func_name": "monitor_lanip",
  "action": "show",
  "param": {
    "TYPE": "conn,conn_num",
    "ip": "10.0.1.2",
    "interface": "all",
    "proto": "all",
    "maxnum": 500,
    "limit": "0,100",
    "ORDER_BY": "",
    "ORDER": ""
  }
}

这一步会返回:

  • 连接数 conn_num
  • 连接详情 conn

它很适合用来做:

  • 终端异常连接排查
  • 可疑外联分析
  • 某台设备当前会话数统计
  • 内网资产行为画像

六、为什么 Demo 不直接打印,而是写入 JSON 文件

SDK 里配了一个示例脚本 sdk/demo.py

它做的事情是:

  1. .env 读取爱快地址、用户名、密码
  2. 登录
  3. 拉终端列表
  4. 遍历每个终端
  5. 拉每个终端的连接详询
  6. 把最终结果写入 demo_result.json

这种方式看起来比 print() 多了一步,但实际上更适合真实使用。

原因有三个

1. 结果可复查

脚本跑完后,数据不会一闪而过。

2. 更适合二次处理

后续脚本、前端页面、分析程序都可以直接读取这个 JSON。

3. 更适合调试接口结构

爱快不同版本返回字段可能不完全一致,保留完整结果文件更方便对照。

七、为什么用 .env 管理配置

Demo 没有把路由器地址和密码写死在代码里,而是从 .env 读取:

IKUAI_ROUTER_URL=http://10.1.1.1
IKUAI_USERNAME=admin
IKUAI_PASSWORD=123

这是一个很小但很重要的改动。

这样做的好处

  • 避免把真实密码写进代码
  • 不同环境切换更方便
  • 便于 .gitignore 忽略
  • 更符合后续接入服务端项目的习惯

这也是为什么 sdk/.gitignore 要把这些文件忽略掉:

__pycache__/
.env
demo_result.json

八、这个 SDK 适合哪些场景

我觉得它特别适合下面这些场景:

1. 内网巡检脚本

定时登录爱快,拉在线终端和连接信息,做健康检查。

2. 安全排查工具

根据终端 IP 查询连接详询,辅助判断异常外联或高连接数设备。

3. 后端服务集成

封装进 Django / Flask / FastAPI 项目,作为爱快侧的数据采集层。

4. 运维自动化

把爱快查询能力接进自动任务、告警系统、资产管理系统。

九、这个 SDK 目前还缺什么

目前它已经能用,但还不算“完整产品化”。

接下来还很值得补的几个方向是:

1. 会话型客户端

比如单独做一个 IKuaiSessionClient,把登录态和后续请求绑定起来。

2. 更多 /Action/call 高层封装

除了终端列表、连接详询,还可以继续补:

  • DHCP
  • DNS
  • 流量统计
  • 接口状态
  • 规则查询

3. 更标准的包结构

比如加上:

  • pyproject.toml
  • 版本号
  • 发布说明
  • 单元测试

4. 更友好的数据格式化

有些爱快返回字段比较原始,可以再做一层清洗和标准化。

十、总结

这个 iKuai SDK 的价值不在于“代码量有多大”,而在于它把一套经常重复写的逻辑稳定下来了:

  • 登录
  • 提取 sess_key
  • 复用 Cookie
  • /Action/call
  • 获取终端列表
  • 获取连接详询

当这些能力被整理成 SDK 之后,后面的 Django 接口、自动化脚本、资产巡检、运维工具,都可以直接站在这个基础上继续做,而不是每次重新研究爱快请求格式。

如果你的项目也要和爱快打交道,我很建议尽早把“能跑的脚本”升级成“可复用的 SDK”。

因为一旦开始复用,后面每多一个功能,成本都会低很多。

附:一个最小可用示例

from ikuai_sdk import IKuaiClient

client = IKuaiClient()

login_result, terminal_result = client.login_and_get_terminal_list(
    router_url="http://10.1.1.1",
    username="admin",
    password="123",
)

terminals = (terminal_result.data or {}).get("data") or []

for device in terminals:
    ip = device.get("ip_addr") or device.get("ip")
    if not ip:
        continue

    connection_result = client.get_terminal_connection_details(
        router_url="http://10.1.1.1",
        cookie_header=login_result.cookie_header,
        ip=ip,
    )

    print(ip, (connection_result.data or {}).get("conn_num"))