Beego本身不内置数据脱敏中间件,需通过OutputFilter在响应序列化前对this.Data["json"]做深拷贝、反射遍历及自定义tag(如desensitize:"mobile")驱动的字段级动态脱敏,仅作用于200状态的application/json响应。

Beego 框架本身不内置数据脱敏中间件,但可以通过自定义 Controller 方法拦截、OutputFilter 或全局 BeforeRouter 钩子,在 JSON 响应序列化前完成字段级动态脱敏。关键不是“加个中间件就完事”,而是控制住响应体生成的时机和结构。
如何在 Beego 的 OutputFilter 中对 JSON 响应脱敏
Beego 的 OutputFilter 是最贴近“中间件语义”的切入点——它在 Controller 执行完毕、准备写入 HTTP 响应体前触发,且能拿到原始 data(即 this.Data 中的 map 或 struct),适合做统一脱敏处理。
- 必须确保脱敏逻辑只作用于
application/json响应,避免干扰文件下载或 HTML 渲染 - 脱敏前需判断
this.Ctx.Output.Status是否为 200 类成功状态,跳过 404/500 等错误响应 - 若
this.Data["json"]是 struct,需用反射遍历字段;若是map[string]interface{},可直接递归处理 - 不要在
OutputFilter中修改this.Data原始值(如直接赋 nil),Beego 内部可能复用该 map,建议深拷贝后处理
BeforeRouter 不适合做字段级脱敏的原因
BeforeRouter 在路由匹配前执行,此时请求还没进 Controller,this.Data 还是空的,根本拿不到要返回的业务数据。强行在这里做脱敏,只能处理 URL 参数或 Header,对响应体无效。
- 常见误用:在
BeforeRouter里调用this.ServeJSON()——这会提前终止流程,导致后续 Controller 不执行,业务逻辑丢失 - 如果想基于用户角色控制是否脱敏,权限判断必须放在
Prepare或Get/Post方法里,而不是BeforeRouter -
BeforeRouter适合做鉴权、日志、跨域头设置,不适合数据变形
结合 json.Marshal 和自定义 tag 实现字段级规则
Beego 默认用标准库 json.Marshal 序列化,因此可复用 Go 原生 struct tag 机制,例如 json:"name,mask" 或 desensitize:"mobile",再配合自定义 marshaler。
- 定义脱敏类型枚举:
mobile、idcard、email,对应 Hutool 风格的掩码逻辑(如手机号留前3后4) - 在
OutputFilter中,对this.Data["json"]做类型断言,遇到 struct 就用reflect扫描字段,检查是否含desensitizetag - 注意:Beego 的
context.Context不透出当前用户信息,需提前把role或deptId存到this.Data或this.Ctx.Input.Data中供脱敏逻辑读取 - 避免在反射中对指针字段做非空判断失败,建议统一要求业务 struct 字段为值类型或显式初始化
为什么不用 Beego 的 Render 或模板脱敏
Beego 的 Render 走的是模板渲染路径(.tpl 文件),适用于 HTML 响应,但现代前后端分离项目基本不用它返回 JSON。硬套模板做 JSON 脱敏会导致:
- JSON 字段名被当作文本渲染,引号和转义失控,极易产出非法 JSON
- 无法复用已有 struct 定义和 tag,需额外维护一套模板变量映射
- 性能开销大:模板解析 + JSON 编码双重序列化
- 与前端约定的字段名不一致风险高(比如模板里写
{{.Phone}},但 API 文档定义的是phone)
真正需要灵活配置“哪些字段脱敏、按什么规则、对谁生效”时,OutputFilter + 反射 + 自定义 tag 是目前 Beego 生态下最可控、最易测试的路径。别指望靠一个注解或配置文件一劳永逸——脱敏逻辑必然耦合权限判断和字段语义,绕不开代码层的显式控制。











