H3C SecPath ACG 1000系列 上网行为管理对接飞书 OAuth2.0 企业认证全流程落地实践

本文基于企业生产真实部署案例,完整记录 H3C SecPath ACG 1000系列 上网行为管理设备通过搭建中间件服务器(172.20.10.1)对接飞书开放平台 OAuth2.0,实现员工飞书扫码一键认证上网的全流程。包含真实配置、生产级代码、交互时序、字段解析、故障排查、生产优化,可直接用于技术分享与现场落地。

前言

企业内网无线认证需替代传统账号密码模式,实现飞书身份一键认证。由于 H3C 设备与飞书开放平台存在接口格式、JSON 结构、参数规范不兼容问题,无法直连对接。本文采用 轻量化异步中间件(FastAPI+httpx) 做协议适配与字段转换,最终实现稳定、高效、可追溯的企业级无线认证方案。

一、核心背景与约束

1. 业务需求

  • 终端用户连接企业网→ 触发 H3C Oauth 认证 → 中间件服务器(我的是Windows,可以是Linux) → 飞书扫码授权 → 自动上网
  • 认证日志本地化、高并发兼容、内存稳定、无控制台乱码

2. 核心技术约束

  • H3C 上网行为管理:仅支持 OAuth2.0 授权码模式,要求扁平 JSON返回(usercode/username/userorg),仅识别顶层字段
  • 飞书开放平台:固定返回嵌套 JSON,接口参数、权限、Token 传递有严格规范
  • 网络环境:异步中间件服务器固定 IP 172.20.10.1,H3C 设备网桥 IP 172.20.0.2(注意看下图,不是带外管理地址)

image

3. 方案选型

  • ❌ 直连方案:H3C ↔ 飞书(格式不兼容,无法解析用户名)

image

  • ✅ 中间件方案:H3C ↔ 异步中间件 ↔ 飞书(协议转换、字段扁平化、稳定性加固)

image

二、整体架构

1. 核心组件

  • H3C 上网行为管理:OAuth2.0 客户端,认证入口与策略控制
  • 异步中间件 (172.20.10.1):基于 FastAPI+httpx 开发的中转服务,承担 H3C 与飞书的协议翻译、字段转换、缓存、日志全功能
  • 飞书开放平台:企业身份源,提供 OAuth2.0 授权、用户信息查询

2. 部署信息

  • 异步中间件服务器:172.20.10.1:8000
  • H3C 设备回调地址:http://172.20.0.2:8000/
  • 飞书应用:企业自建应用

三、OAuth2.0 全交互流程图

image

四、全平台核心配置(真实配置)

1. H3C 上网行为管理 OAuth 配置

注意1:172.20.0.2是我的上网行为带内地址,你要改成你的,只改ip不该后缀。(脚本写死)

注意2:172.20.10.1是我的Windows服务器地址,你也要改成你的,只改ip不该后缀。(脚本写死)

image

配置项 填写参数
服务器名称 飞书认证
描述 H3C 上网行为管理 - 飞书 OAuth 认证
回调地址 http://172.20.0.2:8000/
appID cli_a95XXXXXXXX
appSecret eaQ8UXXXXXXXX
重定向 URL http://172.20.10.1:8000/oauth/authorize
accessToken 获取地址 http://172.20.10.1:8000/oauth/token
accessToken 请求类型 POST
获取用户信息请求地址 http://172.20.10.1:8000/oauth/userinfo
获取用户信息请求类型 POST
获取用户信息所需参数 client_id=cli_a95XXXXXXXX
授权范围 userinfo
获取方式 URL 参数
获取用户关键字 username

2. 飞书开放平台配置

  • 授权地址:https://open.feishu.cn/open-apis/authen/v1/index
  • Token 接口:https://open.feishu.cn/open-apis/authen/v1/access_token
  • 用户信息接口:https://open.feishu.cn/open-apis/authen/v1/user_info
  • 重定向 URL:http://172.20.10.1:8000/callback

image

  • 必开权限:contact:user.base:readonly(用户基础信息只读)

image

3. 异步中间件核心配置

  • 飞书 AppID / AppSecret:生产真实凭证(请修改python中的对应字段)
  • 回调地址:http://172.20.10.1:8000/callback(请修改python中你的服务器地址)
  • Token 过期:60 秒
  • 缓存上限:2000 条(防内存溢出)
  • 日志路径:D:\login_log.txt

五、生产级异步中间件完整代码(Python)

from fastapi import FastAPI, Request, Cookie, Form, HTTPException
from fastapi.responses import RedirectResponse
import httpx
import uuid
import datetime
from cachetools import TTLCache
import asyncio
import urllib3

# 关闭SSL证书警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

app = FastAPI(title="H3C飞书认证服务")

# ====================== 核心配置(172.20.10.1需要改成你的服务器地址) ======================
FEISHU_APP_ID = "cli_a95XXXXXXX"
FEISHU_APP_SECRET = "eaQ8UXXXXXXX"
CALLBACK_URL = "http://172.20.10.1:8000/callback"
TOKEN_EXPIRE_SECONDS = 60
LOG_FILE = r"D:\login_log.txt"

# ====================== 内存缓存(自动过期、防溢出) ======================
user_cache = TTLCache(maxsize=2000, ttl=TOKEN_EXPIRE_SECONDS)
cache_lock = asyncio.Lock()

# ====================== 日志模块(成功/失败分离) ======================
def write_success_log(name, user_id, mobile, ip):
    now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    log = f"[{now}] 姓名={name} | 用户ID={user_id} | 手机号={mobile} | IP地址={ip}\n"
    try:
        with open(LOG_FILE, "a", encoding="utf-8") as f:
            f.write(log)
    except Exception:
        pass

def write_error_log(msg):
    now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    log = f"[{now}] ERROR {msg}\n"
    try:
        with open(LOG_FILE, "a", encoding="utf-8") as f:
            f.write(log)
    except Exception:
        pass

# ====================== 异步HTTP客户端 ======================
client = httpx.AsyncClient(timeout=5, verify=False)

# ====================== 飞书接口重试机制(3次重试,抗网络波动) ======================
async def feishu_request(method, url, **kwargs):
    for i in range(3):
        try:
            resp = await client.request(method, url, **kwargs)
            resp.raise_for_status()
            return resp.json()
        except Exception as e:
            if i == 2:
                write_error_log(f"飞书接口请求失败: {str(e)}")
                raise
            await asyncio.sleep(0.5 * (i + 1))

# ====================== 核心业务接口 ======================
# 1. H3C授权入口:重定向飞书授权页
@app.get("/oauth/authorize")
async def authorize(request: Request, redirect_uri: str = ""):
    url = (
        "https://open.feishu.cn/open-apis/authen/v1/index?"
        f"app_id={FEISHU_APP_ID}"
        f"&redirect_uri={CALLBACK_URL}"
        f"&response_type=code"
    )
    response = RedirectResponse(url)
    # 存储H3C回调地址
    response.set_cookie("h3c_redirect", redirect_uri, max_age=120)
    return response

# 2. 飞书授权回调:处理code、获取用户信息
@app.get("/callback")
async def callback(code: str, request: Request, h3c_redirect: str = Cookie(None)):
    try:
        # 授权码兑换Access Token
        token_data = await feishu_request(
            "POST",
            "https://open.feishu.cn/open-apis/authen/v1/access_token",
            json={
                "app_id": FEISHU_APP_ID,
                "app_secret": FEISHU_APP_SECRET,
                "code": code,
                "grant_type": "authorization_code"
            }
        )
        access_token = token_data["data"]["access_token"]

        # 获取飞书用户信息
        user_data = await feishu_request(
            "GET",
            "https://open.feishu.cn/open-apis/authen/v1/user_info",
            headers={"Authorization": f"Bearer {access_token}"}
        )
        data = user_data["data"]

        # 提取用户字段
        username = data.get("name", "")
        user_id = data.get("user_id", "")
        mobile = data.get("mobile", "")
        client_ip = request.client.host

        # 记录成功日志
        write_success_log(username, user_id, mobile, client_ip)

        # 生成自定义Token
        user_token = f"USER_{user_id}_{uuid.uuid4().hex[:8]}"

        # 存入缓存
        async with cache_lock:
            user_cache[user_token] = {
                "usercode": user_id,
                "username": username,
                "userorg": ""
            }

        # 回调H3C设备
        if h3c_redirect:
            return RedirectResponse(f"{h3c_redirect}&code={user_token}&state=STATE")
        return {"code": user_token}

    except Exception as e:
        write_error_log(f"认证失败: {str(e)}")
        raise HTTPException(status_code=500, detail="认证失败")

# 3. H3C获取Token接口
@app.post("/oauth/token")
async def token(code: str = Form(None)):
    return {
        "access_token": code,
        "token_type": "bearer",
        "expires_in": TOKEN_EXPIRE_SECONDS
    }

# 4. H3C获取用户信息接口(扁平JSON,适配H3C)
@app.post("/oauth/userinfo")
async def userinfo(access_token: str = Form(None)):
    try:
        async with cache_lock:
            return user_cache.get(access_token, {
                "usercode": "",
                "username": "",
                "userorg": ""
            })
    except Exception as e:
        write_error_log(f"userinfo异常: {str(e)}")
        return {
            "usercode": "",
            "username": "",
            "userorg": ""
        }

# ====================== 服务启动(生产优化) ======================
if __name__ == "__main__":
    import uvicorn

    # 关闭彩色日志,避免Windows乱码
    log_config = uvicorn.config.LOGGING_CONFIG
    log_config["formatters"]["default"]["use_colors"] = False
    log_config["formatters"]["access"]["use_colors"] = False

    print("==================================================")
    print("H3C飞书认证服务启动成功(日志定制版)")
    print("服务地址:http://0.0.0.0:8000")
    print(f"日志文件:{LOG_FILE}")
    print("==================================================")

    uvicorn.run(
        app,
        host="0.0.0.0",
        port=8000,
        log_config=log_config,
        log_level="warning"
    )

六、核心字段映射与交互解析

1. 飞书 → 异步中间件 字段转换

飞书嵌套字段 中间件扁平字段 用途
data.name username H3C 展示用户名
data.user_id usercode 用户唯一标识
官方 Access Token 自定义临时 Token 会话凭证

2. 关键接口解析

  • /oauth/authorize:接收 H3C 授权请求,重定向飞书扫码页
  • /callback:飞书授权回调,处理 code、获取用户信息、格式化数据
  • /oauth/token:返回 H3C 要求的标准 OAuth2.0 Token
  • /oauth/userinfo:返回 H3C 兼容的扁平 JSON 用户信息

七、生产常见故障与解决方案

1. 报错:网页解析失败,可能是不支持的网页类型

  • 根因:H3C 仅识别纯 JSON 响应,不兼容 HTML / 文本 / 错误状态码
  • 方案:中间件严格返回 JSON,无多余输出,状态码 200

2. 报错:飞书提示「请求非法」

  • 根因:飞书重定向 URL 与中间件 CALLBACK_URL 不一致
  • 方案:飞书后台配置 http://172.20.10.1:8000/callback

3. 间歇性认证失败

  • 根因:服务器 DNS 解析不稳定、网络波动
  • 方案:中间件代码加入 3 次重试机制

八、生产环境优化要点

  1. 异步非阻塞架构:FastAPI+httpx 异步处理,单机支撑大量并发认证
  2. 内存安全:TTLCache 限制缓存条数 + 自动过期,内存占用低且稳定
  3. 日志优化:成功 / 失败日志分离,本地化存储,无控制台乱码
  4. 稳定性:飞书接口 3 次重试,抵御网络波动
  5. 兼容性:Windows / Linux 全平台运行,无环境依赖

九、总结

H3C 上网行为管理与飞书 OAuth2.0 无法直接直连,核心瓶颈为飞书嵌套 JSONH3C 扁平 JSON的格式冲突、接口参数规范不兼容。
轻量化异步中间件是生产级可行方案,作为唯一中转服务承担协议适配、字段转换、缓存管理、日志追溯全功能,完美实现企业员工飞书扫码一键认证上网,方案已通过长时间、高并发生产验证,稳定可靠、易部署、易维护。

附录:依赖安装命令

pip install fastapi uvicorn httpx cachetools

记得:

Windows服务器放行8000端口,用管理员身份打开 PowerShell,复制运行这一条命令:

New-NetFirewallRule -DisplayName "PythonAuthServer-8000" -Direction Inbound -Protocol TCP -LocalPort 8000 -Action Allow

Linux服务器放行8000端口

Ubuntu 

sudo ufw allow 8000/tcp

CentOS

sudo firewall-cmd --permanent --add-port=8000/tcp

PS:20260416 该代码可行,如果飞书换了接口,可以把我的脚本扔给AI,让它帮你改,这里有特殊处理转化,不多做解释。

 

 

日常操作指南

1 验证服务是否运行

  • 方式1:浏览器访问 http://172.20.10.1:8000,能看到这个页面代表运行正常

image

  • 方式2:打开任务管理器→详细信息,找到 exe 进程即运行正常
  • 方式3:飞书扫码认证,查看 D:\login_log.txt 是否新增日志

2 查看登录日志

日志路径:D:\login_log.txt(UTF-8编码,可直接用记事本打开)

日志内容:登录时间、姓名、用户ID、手机号、原始飞书JSON数据(追加写入,不覆盖)

3 手动启动/停止服务

  • 手动启动:CMD 进入 D 盘,执行双击py(前台运行,关闭CMD即停止)
  • 手动停止:任务管理器→详细信息→找到 exe→结束任务

4 重启服务

  1. 任务管理器结束exe 进程
  2. CMD 执行 python D:\feishuOauth.py,或服务器重启后自动启动

常见问题排查

5.1 服务无法访问(浏览器打不开 http://172.20.10.1:8000)

  • 服务器检查 python 进程是否运行,未运行则手动启动

5.2 开机自启失败(重启服务器后服务未运行)

  • 排查1:检查任务计划程序中,任务是否勾选“使用最高权限运行”“不管用户是否登录都要运行”
  • 排查2:检查任务计划的“操作”中,参数和起始于路径是否正确(参数:D:\feishuOauth.py,起始于:D:\)
  • 排查3:重新创建任务计划(百度一下python脚本开机自动运行的方法,可以开机自动运行)。
posted @ 2026-04-16 17:58  Magiclala  阅读(15)  评论(0)    收藏  举报