---
title: GraphQL字段级审计与策略联动最佳实践
keywords:
- GraphQL
- 字段审计
- Resolver Hook
- Scope
- OperationName
- Persisted Query
- Metrics
- Policy
- 追踪
- 聚合
description: 构建字段级审计与策略联动方案,记录字段访问与用户上下文,按Scope策略联动拦截并聚合指标(操作名/持久化查询),附Resolver钩子与守卫示例。
categories:
- 文章资讯
- 技术教程
---
一、策略与上下文
type Token = { sub: string; scope: string[] }
const fieldScope: Record<string, string> = { email: 'read:email', ssn: 'read:ssn', balance: 'read:balance' }
function hasScope(tok: Token, need: string): boolean { return tok.scope.includes(need) }
二、审计钩子
type Audit = { id: string; userId: string; field: string; operation: string; persistedId?: string; timestamp: string }
function nowIso(): string { return new Date().toISOString() }
function auditField(userId: string, field: string, operation: string, persistedId?: string): Audit {
return { id: String(Date.now()) + Math.random().toString(36).slice(2), userId, field, operation, persistedId, timestamp: nowIso() }
}
三、守卫与联动
function guardField(tok: Token, fields: string[]): boolean {
for (const f of fields) { const need = fieldScope[f]; if (need && !hasScope(tok, need)) return false }
return true
}
四、聚合指标
class Metrics {
counts = new Map<string, number>()
inc(key: string) { const n = (this.counts.get(key) || 0) + 1; this.counts.set(key, n) }
get(key: string): number { return this.counts.get(key) || 0 }
}
function keyOf(operation: string, field: string): string { return `${operation}:${field}` }
五、整合示例
type Req = { body: { query: string; operationName?: string; persistedId?: string }; token: Token }
type Res = { status: (n: number) => Res; end: (b?: string) => void }
function extractFields(query: string, allow: string[]): string[] { const out: string[] = []; for (const f of allow) { const re = new RegExp(`\\b${f}\\b`); if (re.test(query)) out.push(f) } return out }
function resolverGuard(req: Req, res: Res, metrics: Metrics): boolean {
const op = req.body.operationName || 'unknown'
const id = req.body.persistedId
const fields = extractFields(req.body.query, Object.keys(fieldScope))
if (!guardField(req.token, fields)) { return res.status(403).end('forbidden'), false }
for (const f of fields) metrics.inc(keyOf(op, f))
return true
}
function resolverAudit(req: Req): Audit[] {
const op = req.body.operationName || 'unknown'
const id = req.body.persistedId
const fields = extractFields(req.body.query, Object.keys(fieldScope))
return fields.map(f => auditField(req.token.sub, f, op, id))
}
六、验收清单
- 字段级Scope策略联动拦截;未授权字段拒绝且记录审计。
- 指标按
operation:field聚合;持久化查询ID参与审计与追踪。 - Resolver钩子输出包含用户与字段、操作名与时间戳;与守卫协同生效。

发表评论 取消回复