项目进展2026-09-23

志睿源官网|后台管理入口「免密网关」上线报告

把公众号菜单直连后台地址的裸入口,换成有身份的微信静默授权网关:管理员免密直达、非管理员看不到登录页、后台地址不外露。

  • 日期:2026-09-23
  • 范围:志睿源官网后台(/zry/admin/)的入口收敛
  • 状态:✅ 已上线并通过全部接口侧核验;真机端到端实测待完成
  • 关联:前序评估见《公众号菜单_官网管理入口_评估与设计》

一、要解决的问题

公众号菜单里新增的「官网管理」原本直连后台地址,等于对全部关注者公布了后台入口:任何人点开都能看到登录页,后台地址也被动外泄。

M2 的目标是把这条路改成有身份的门:

要求实现效果
管理员免密进入点菜单 → 微信静默授权 → 直接落在后台首页,无需输账号密码
非管理员直接拒绝连登录页都看不到,只回一个「未授权访问」提示页
后台地址不外露后台真实路径不再出现在菜单里,菜单只带授权入口
不引入新依赖复用站点既有鉴权与数据库,不新增服务、不新增端口

二、授权链路

用户点「管理 → 官网管理」
        │
        ▼
  授权发起端点  ── 生成一次性 state(内存表,TTL 5 分钟,用完即删)
        │
        ▼  302
  微信静默授权(snsapi_base,用户无感知,不弹确认框)
        │
        ▼  携带 code + state 回调
  授权回调端点
        ├─ state 校验失败 ──────────────► 提示「链接已失效」
        ├─ code 换 openid
        ├─ 白名单查不到 ────────────────► 「未授权访问」(显示本人授权码,便于加白)
        └─ 命中白名单 且 账号启用
                 │
                 ▼
           签发站点 JWT(短时票据)
                 │
                 ▼ 302 到后台首页,票据放在 URL fragment
           后台首页脚本把票据落到本地存储,并立刻抹除地址栏上的 fragment

为什么用 fragment 携带票据:# 后面的内容不会发给服务器——不进访问日志、不进 Referer、不落任何中间件。落到本地存储后立即用 replaceState 抹掉,地址栏干净。

为什么用 snsapi_base:静默授权,用户全程无感知,不会弹出「是否允许」确认页;代价是只能拿 openid,拿不到昵称头像——而网关只需要 openid 做身份判定,刚好够用。


三、六处落地

#位置改动
①网关模块(新建)授权发起、回调核验、白名单判定、票据签发,一个文件自洽
②路由注册表新增两条公开路由:授权发起 / 授权回调
③模块聚合入口把网关的两个函数显式具名注入导出对象
④反向代理配置新增两条精确匹配转发规则,早于通用静态规则生效
⑤后台首页增加「fragment → 本地存储 → 抹除地址」的三行注入脚本
⑥菜单真源管理 → 官网管理 的目标地址改指网关入口

要点:

  • ③ 之所以「显式具名」而不是靠对象展开批量注入:聚合入口用展开语法时,新增模块容易漏挂而不报错,只表现为接口 404,排查成本高。具名写出,缺失即启动报错。
  • ④ 的精确匹配是必须的,见第五节的坑 2。
  • ⑤ 只加在后台首页,不影响其它页面。

四、白名单机制

新建一张绑定表,把微信身份与后台账号关联起来:

字段含义
openid微信身份(主键)
admin_id对应的后台账号
note备注(谁)
created_at加入时间
last_login_at最近一次免密登录时间

放行条件=该 openid 有绑定记录 且 对应后台账号处于启用状态。

放行时的副作用(便于事后审计):

  • 回写绑定的 last_login_at;
  • 更新账号的 last_login_at 与 last_login_ip;
  • 写一条 login 类型的操作日志,与密码登录共用同一套日志表——从审计角度看,免密登录和密码登录是同一件事。

加人:往绑定表插一行 (openid, admin_id, note) 即可,无需改代码、无需重启。被拒页面会显示本人的「授权码」(就是 openid),用户截图发来即可加白——这也是为什么拒绝页要显示它。


五、三个坑(都已规避)

坑 1(最重要):防护层会把网关的合法拒绝当成攻击

站点的防护规则里,判断依据是「响应 403 / 429 即视为攻击」。这条规则成立的前提是:403 只可能来自反代的静态层拦截,真人根本碰不到。

网关打破了这个前提——「非管理员来访」是设计内的合法 403。后果很严重:

一个非管理员用户连点 5 次菜单,就会触发 IP 级封禁 24 小时,连官网首页都打不开。 管理员自己扫码失败、反复重试,同样会中招。

解法:给防护规则加精确豁免,只放过这两条网关路径,其余一切照旧拦截。并用规则回放工具验证:

3 行样本 → 1 行忽略(网关的 403,正确放过)
          → 1 行命中(探测敏感文件的 403,防护未受损)
          → 1 行未命中

关键是豁免范围必须收窄到路径级——如果图省事按状态码或按目录放宽,防护就是白做的。

坑 2:挂在静态目录路径下的动态端点会被当成静态文件

网关端点位于后台页面的子路径下,而该路径整体被一条「静态文件」规则接管。若只在明细里追加转发规则,请求会先被静态规则捕获 → 找文件 → 404。

解法:用「精确匹配」的转发规则,优先级高于前缀匹配,命中即转发,不再落到静态逻辑。

坑 3:回调路由必须走「无数据库事务」的变体

回调端点自己结束响应(302),不走框架的统一事务封装。若按普通路由注册,响应结束后连接不会归还连接池,连接数只增不减,最终全站卡死(该站点此前已因此出过一次事故)。

解法:两条新路由都注册为「无事务」变体,数据库连接在网关内部自取自还。


六、上线核验结果

核验项结果
菜单线上真值回读✅ 与前序方案逐字一致;菜单开关正常;无个性化菜单残留
授权发起(要求返回链接)✅ 200,参数完整(appid / 回调地址 / 静默作用域 / state)
授权发起(要求跳转)✅ 302 指向微信授权页
回调(伪造 state)✅ 403,提示链接已失效
白名单判定(真身份)✅ 放行,账号启用
白名单判定(假身份)✅ 拒绝,无绑定记录
后台首页 / 官网首页 / 博客 / 公开接口✅ 全部 200,回归无影响
反代配置语法✅ 通过并平滑重载
事故影响面✅ 网关落地后无人被误封

七、待办

  1. 真机端到端实测(只能由本人做):在微信里点「管理 → 官网管理」,确认免密直达后台。

接口侧无法模拟微信授权,这一步必须真机验证。

  1. 若被拒:把拒绝页显示的「授权码」发来,加一行白名单即可。
  2. 生效延迟提示:已关注的老用户,客户端菜单缓存最长 24 小时刷新;新关注立即生效。

附:可回滚点

本次共改动 6 个文件,每个都在改动前留有同目录同时间戳的备份,覆盖回去即回到改动前状态;新建的绑定表不影响任何既有逻辑,回滚时可保留(不影响旧功能)。

日志格式:<文件名>.bak-<日期>。

← 返回文章列表