Documentation
¶
Overview ¶
auditactor.go 审计操作者提取:从请求上下文取 (用户ID, 用户名, IP)。
此前该逻辑以 actorFromCtx 内嵌于 admin 域;config 域独立后两个域都要写 管理操作审计(admin_operation_logs 同表),提取逻辑下沉 pkg 层单源 (AuthMiddleware 写入的身份 value 本就由本包提供)。
Package middleware 提供 HTTP 中间件。
本文件实现 Prometheus 指标采集中间件,暴露 HTTP RED 指标:
- RequestsTotal(Counter):请求总数,按 method/path/status 打标
- RequestDurationSeconds(Histogram):请求延迟分布
- RequestsInFlight(Gauge):当前处理中的请求数
指标通过 /metrics 端点暴露(由 bootstrap customizedRegister 注册 promhttp.Handler)。
Package middleware 提供 Hertz 中间件:
- RateLimit: IP 维度 + 接口维度双层限流
实现:默认进程内 token bucket——per-IP 桶由 oekit/concurrency/ratelimit 的 KeyedLimiter 托管(工厂造桶 + lastSeen TTL 自动清死桶,底层 x/time/rate); 配置 rate_limit.use_redis=true 且 Redis 可用时切换为 Redis 固定窗口计数 (多实例共享)。命中限流后按 block_seconds 封禁该 IP+维度(封禁同样 Redis/内存双写)。配置可经 /admin/ratelimit/config 运行时热更。
Index ¶
- Constants
- Variables
- func APIKeyIDFromContext(ctx context.Context) (uint, bool)
- func APIKeyScopeAllowed(scopes []string, need string) bool
- func AccessLog() app.HandlerFunc
- func AdminMiddleware() app.HandlerFunc
- func AdminOrAdminKey() app.HandlerFunc
- func AuditActor(ctx context.Context) (id *uint, name, ip string)
- func AuthMiddleware() app.HandlerFunc
- func CSRFAllowed(c *app.RequestContext, viaCookie bool) bool
- func ClearSessionCookie(c *app.RequestContext)
- func ClientIP(c *app.RequestContext) string
- func ClientIPFromContext(ctx context.Context) string
- func FormatLockKey(parts ...string) string
- func IdentityFresh(ctx context.Context, claims *auth.Claims) bool
- func InvalidateIdentity(userID uint)
- func LookupLockKey(scope, ip, code string) string
- func MetricsMiddleware() app.HandlerFunc
- func OptionalAPIKey() app.HandlerFunc
- func OptionalAuthMiddleware() app.HandlerFunc
- func OptionalIdentity() []app.HandlerFunc
- func Recovery() app.HandlerFunc
- func RequestID() app.HandlerFunc
- func RequiredAPIKeyScope(path string) string
- func ResolveClientIP(remote, xff string) string
- func RoleFromContext(ctx context.Context) string
- func SecurityHeaders() app.HandlerFunc
- func SessionToken(c *app.RequestContext) (string, bool)
- func SetAPIKeyStore(s APIKeyStore)
- func SetIdentityHeaders(c *app.RequestContext, claims *auth.Claims)
- func SetIdentityLoader(fn func(ctx context.Context, userID uint) (*IdentityRecord, error))
- func SetJWTClaims(c *app.RequestContext, claims *auth.Claims)
- func SetSecurityHeadersConfig(cfg SecurityHeadersConfig)
- func SetSessionCookie(c *app.RequestContext, token string, maxAgeSeconds int)
- func SetTrustedProxies(cidrs []string) error
- func TraceIDFromContext(ctx context.Context) string
- func UserIDFromContext(ctx context.Context) (uint, bool)
- func UserOrAPIKey() app.HandlerFunc
- func UsernameFromContext(ctx context.Context) string
- func ValidateAPIKeyScopes(scopes []string) ([]string, error)
- type APIKeyPrincipal
- type APIKeyStore
- type CtxKeyTraceID
- type IdentityRecord
- type LockedError
- type Lockout
- type Metrics
- type RateLimitConfig
- type RateLimiter
- func (rl *RateLimiter) DownloadMiddleware() app.HandlerFunc
- func (rl *RateLimiter) GlobalMiddleware() app.HandlerFunc
- func (rl *RateLimiter) LoginMiddleware() app.HandlerFunc
- func (rl *RateLimiter) Middleware(s scope) app.HandlerFunc
- func (rl *RateLimiter) ProbeAllow(ctx context.Context, s scope, probeIP string) bool
- func (rl *RateLimiter) SetRedis(rdb *redis.Client)
- func (rl *RateLimiter) SnapshotConfig() RateLimitConfig
- func (rl *RateLimiter) Stats() map[string]interface{}
- func (rl *RateLimiter) Stop()
- func (rl *RateLimiter) UpdateConfig(cfg RateLimitConfig)
- func (rl *RateLimiter) UploadMiddleware() app.HandlerFunc
- type ScopeError
- type SecurityHeadersConfig
Constants ¶
const ( // ScopeAdmin 管理面(/admin/**、/api/v1/admin/**)。 ScopeAdmin = "admin" // ScopeUser 用户自有资源(/user/**、/api/v1/user/**)。 ScopeUser = "user" ScopeShare = "share" )
===== API Key 权限范围(scopes,2026-10-30)=====
用户级 API Key 此前是**全量令牌**:挂到 MCP / CI / 集成方即全权, 无法「只给寄件权限」。本次给 Key 加「权限范围」:创建时指定该 Key 被允许的 能力面,服务端按**请求路径**判定所需能力,缺该能力 ⇒ 403。
**缺省 / 空 = 全量** ⇒ 与本机制引入前行为完全一致(老 Key 无需重建、存量部署零影响)。
const ( // SessionCookieName 会话 Cookie 名(host-only,不设 Domain) SessionCookieName = "pb_token" // CSRFHeaderName Cookie 认证下非安全方法的必备请求头 CSRFHeaderName = "X-Requested-With" // CSRFHeaderValue 该请求头的期望值 CSRFHeaderValue = "XMLHttpRequest" )
const TraceIDHeader = "X-Trace-Id"
TraceIDHeader 请求链路 ID 透传 header,与 internal/pkg/resp 对齐。
Variables ¶
var AllAPIKeyScopes = []string{ScopeAdmin, ScopeUser, ScopeShare}
AllAPIKeyScopes 全部合法取值(管理台下拉的数据源;校验也用它)。
Functions ¶
func APIKeyIDFromContext ¶
APIKeyIDFromContext 从 ctx 读取认证所用的 API Key ID(JWT/匿名请求返回 false)。
func APIKeyScopeAllowed ¶ added in v0.32.31
APIKeyScopeAllowed 判断该 Key 是否被允许访问所需能力面。 scopes 为空 = 全量(老 Key / 未指定范围的 Key)。
func AccessLog ¶
func AccessLog() app.HandlerFunc
AccessLog 结构化访问日志中间件。
记录每个请求的 method/path/status/ip/latency/trace_id,便于生产环境排障。 与 RequestID 中间件配合:trace_id 由 RequestID 写入 ctx 与 RequestContext, 本中间件从 RequestContext 读取并打入日志字段,实现日志与 resp envelope 的 trace_id 一致,方便串联请求链路。
func AdminMiddleware ¶
func AdminMiddleware() app.HandlerFunc
AdminMiddleware 管理员权限中间件
实现(2026-10-05 审计 P0 修复):认证+授权必须在本中间件内联完成后统一放行。 此前复用 AuthMiddleware()——其成功路径会 c.Next 把链上剩余 handler(含业务 handler)先执行完,返回后才做角色检查:写操作已生效、读操作的响应体已生成 (hertz 的 JSON 写入是追加语义,403 只会拼在业务响应后面)。任意注册用户 因此可执行全部 /admin/* 与 /api/v1/mcp。
func AdminOrAdminKey ¶ added in v0.20.0
func AdminOrAdminKey() app.HandlerFunc
AdminOrAdminKey 管理面自动化通道(2026-10-14 P3):admin JWT(会话/Bearer) 或 **admin 用户**的 API Key(ApiKey 头 / Bearer pbk_* / X-API-Key)。 与 UserOrAPIKey 边界(本文件 §4.1「绝不挂 /admin」)的区分:本中间件的 Key 路径强制 admin 角色复核——非管理员的 Key 一律 403;唯一消费方是 pb CLI 的 直传自动化(/admin/p2p/* 组),不开普通 Key 进管理面的口子。
func AuditActor ¶ added in v0.15.1
AuditActor 从请求上下文提取审计操作者:
- id: 登录用户 ID(未登录为 nil)
- name: 用户名;定时任务等非请求场景无身份,记为 "system"
- ip: 可信代理解析后的客户端 IP
func AuthMiddleware ¶
func AuthMiddleware() app.HandlerFunc
AuthMiddleware JWT认证中间件
令牌来源:Authorization: Bearer 头或会话 Cookie(pb_token,浏览器端默认; Cookie 认证的非安全方法另有 CSRF 头门禁,见 session.go)。
func CSRFAllowed ¶
func CSRFAllowed(c *app.RequestContext, viaCookie bool) bool
CSRFAllowed Cookie 认证的 CSRF 门禁:安全方法(GET/HEAD/OPTIONS)放行; 非安全方法要求携带自定义头 X-Requested-With: XMLHttpRequest。
func ClearSessionCookie ¶
func ClearSessionCookie(c *app.RequestContext)
ClearSessionCookie 清除会话 Cookie(logout/会话失效时调用)。
func ClientIP ¶
func ClientIP(c *app.RequestContext) string
ClientIP 解析客户端真实 IP(可信代理解析,XFF 从右向左)。
规则:
- 直连地址不在可信代理网段 → 返回直连地址(忽略一切代理头)
- 直连地址可信 → 从 X-Forwarded-For 最右端向左扫描, 跳过可信代理地址,返回第一个不可信地址(即真实客户端)
- XFF 缺失或整条链都没有合法候选 → 返回直连地址
**不采信 X-Real-IP**:单跳头无法区分「代理写的」与「客户端自带的」——网关 默认不写该头但会原样透传客户端自带的,采信它等于让客户端任意改写自己的 客户端 IP(重置匿名配额 / 改写审计归因)。上游 oekit/pkg v0.26.0 起默认 同样不采信(oekit/pkg#106)。
func ClientIPFromContext ¶
ClientIPFromContext 从 ctx 读取已解析的客户端 IP
func IdentityFresh ¶ added in v0.14.9
IdentityFresh 导出的身份复核入口:AuthMiddleware 之外的认证路径 (如 transport 层 UserAuth)共享同一 30s 缓存回查——封禁/降权/改密后 旧 token 不再在这些路由越界可用(2026-10-08 加固)。
func InvalidateIdentity ¶
func InvalidateIdentity(userID uint)
InvalidateIdentity 清除某用户的回查缓存(改密/封禁/降权落库后调用, 使本次变更绕过 TTL 立即生效)。
func LookupLockKey ¶ added in v0.14.7
LookupLockKey 取件/分享码查询的失败锁定键:查询折叠开启(download. code_case_insensitive,默认开)时把 code 归一为大写,使 abc/ABC 等大小写 变体共享同一失败计数,防变体绕过 IP+code 维度爆破锁定。
func MetricsMiddleware ¶
func MetricsMiddleware() app.HandlerFunc
MetricsMiddleware 采集 HTTP RED 指标的中间件。 必须在 NewMetrics() 之后、路由注册之前通过 h.Use 注册为全局中间件。
func OptionalAPIKey ¶
func OptionalAPIKey() app.HandlerFunc
OptionalAPIKey 可选 API Key 认证(fail-closed:携带即校验,无效一律 401/429)
func OptionalAuthMiddleware ¶
func OptionalAuthMiddleware() app.HandlerFunc
OptionalAuthMiddleware 可选认证中间件(不强制要求登录)
func OptionalIdentity ¶
func OptionalIdentity() []app.HandlerFunc
OptionalIdentity 可选身份认证组:JWT(既有可选语义)→ API Key(fail-closed)。 未携带凭证 → 匿名;携带 Key 但无效 → 401;JWT 无效 → 匿名(会话过期 UX,与 Key 有意不对称)。 供 gen 路由挂载点整体替换 OptionalAuthMiddleware()(挂载函数直接 return 本切片)。
func Recovery ¶
func Recovery() app.HandlerFunc
Recovery 全局 panic 恢复中间件。
捕获 handler 链中任何 panic,记录堆栈与 trace_id,并返回统一 500 响应, 防止单个请求的 panic 导致整个进程崩溃(Hertz 默认不保证全链路 recover)。
必须注册在其他中间件之前,以覆盖后续所有 handler。
func RequestID ¶
func RequestID() app.HandlerFunc
RequestID 为每个请求生成 / 透传 trace_id(X-Trace-Id)。
行为:
- 沿用请求头 X-Trace-Id(须通过白名单校验:长度 ≤64、字符集 [A-Za-z0-9._-],见 resp.ValidTraceID。2026-10-06 审计:原样回显 任意值会把超长串写进响应头并落入访问日志——日志刷量/注入面)
- 缺失或不合规时生成 UUID
- 写入响应头、app.RequestContext(Set)与 context.Context, 使访问日志 / 业务日志 / resp envelope 都能拿到同一个 trace_id。
与 resp.getOrGenTraceID 配合:resp 包在构造响应体时会读取同样的 header(同样过白名单),保证全链路 trace_id 一致。
func RequiredAPIKeyScope ¶ added in v0.32.31
RequiredAPIKeyScope 返回该路径所需的权限范围(空串 = 不要求该能力)。
func ResolveClientIP ¶
ResolveClientIP 可信代理解析核心(纯函数,便于单测覆盖伪造场景)。 remote 为直连对端地址(host:port 或裸 IP);xff 为原始代理头。
采信判定与 XFF 可信链扫描收编上游 oekit/pkg/convert.ResolveClientIP (v0.25.2 随 #95 落地,v0.26.0 随 #104/#106/#107 收紧)——与 bianque 等 消费方共用同一份实现,避免各仓各写一遍后口径漂移。
本仓此前自持的两处加固已由上游补齐,本地不再重复实现:
- 代理头候选值合法性校验——任意字符串进限流/锁定键与审计日志,既是 「伪造 IP 永不命中计数 = 绕过」,也可注入任意内容污染日志;
- 候选值带端口时归一为裸 IP——同一客户端换个端口就能换出一个新桶。
空对端地址返回 "unknown"(本仓约定:调用方按字符串拼进限流/锁定键, 空串会让"未知来源"与"真实空值"混在一起)。
func SessionToken ¶
func SessionToken(c *app.RequestContext) (string, bool)
SessionToken 提取会话令牌:Authorization: Bearer 头优先,回退会话 Cookie。 返回 (token, viaCookie)。
func SetIdentityHeaders ¶
func SetIdentityHeaders(c *app.RequestContext, claims *auth.Claims)
SetIdentityHeaders 身份响应头三连(X-User-ID/X-Username/X-Role)。
func SetIdentityLoader ¶
func SetIdentityLoader(fn func(ctx context.Context, userID uint) (*IdentityRecord, error))
SetIdentityLoader 注入身份加载器(bootstrap 调用一次;nil = 关闭回查, 退化为纯 JWT 语义——单测/轻量环境)。
func SetJWTClaims ¶
func SetJWTClaims(c *app.RequestContext, claims *auth.Claims)
SetJWTClaims JWT 身份 c.Set 四件套(键名单一真相源:user_id/username/role/auth_type, 与 transport 侧 ContextKey* 常量同字符串)。pkg 与 transport 两个中间件栈均委托此处。
func SetSecurityHeadersConfig ¶
func SetSecurityHeadersConfig(cfg SecurityHeadersConfig)
SetSecurityHeadersConfig 设置安全头全局配置(bootstrap 调用)。
func SetSessionCookie ¶
func SetSessionCookie(c *app.RequestContext, token string, maxAgeSeconds int)
SetSessionCookie 下发会话 Cookie(maxAgeSeconds<=0 视为会话期 Cookie)。
func SetTrustedProxies ¶
SetTrustedProxies 配置可信代理网段(CIDR 或单 IP)。 空列表 = 不信任任何代理头(直连部署的安全默认)。
条目解析收编上游 oekit/pkg/convert.TrustedProxySet(pkg v0.26.0):
- 裸 IP 归一为单主机网段(IPv4 /32、IPv6 /128);
- 非法条目**构造期报错**,不静默跳过——静默跳过把「配置写错」变成 「安全策略降级」且不可观测(此前裸 IP 被直接 continue 掉:配置不生效 且无任何告警,oekit/pkg#104)。
解析在构造期一次完成,请求期只做网段包含判定——此前每请求都要把整份清单 逐条 ParseCIDR 一遍(oekit/pkg#107)。
func TraceIDFromContext ¶
TraceIDFromContext 从 context.Context 提取 trace_id(service / 日志用)。 委托 logger 同名函数(真相源),保留本包导出兼容既有调用点。
func UserIDFromContext ¶
UserIDFromContext 从 ctx 读取用户 ID(未认证返回 false)
func UserOrAPIKey ¶
func UserOrAPIKey() app.HandlerFunc
UserOrAPIKey 登录凭证二选一:API Key 或 JWT(含黑名单检查,复用 AuthMiddleware 完整语义)。 供 /api/v1 自定义 REST 组替换 transport UserAuth,向第三方开放 Key 访问。 边界(设计文档 §4.1):绝不挂 /admin;/user/api-keys 保持 JWT-only(Key 不能管 Key)。
func UsernameFromContext ¶
UsernameFromContext 从 ctx 读取用户名(未认证返回空串)
func ValidateAPIKeyScopes ¶ added in v0.32.31
ValidateAPIKeyScopes 校验并归一化权限范围(去空白/去重;空 = 全量;非法值报错)。
非法值**直接报错**而不是悄悄忽略:用户以为自己签发的是受限 Key, 实际拿到全量 ⇒ 安全语义被静默放宽(与「旗标缺值被吞」同类事故)。
Types ¶
type APIKeyPrincipal ¶
type APIKeyPrincipal struct {
KeyID uint
UserID uint
Username string
Role string
// Scopes 该 Key 被允许的能力面(空 = **全量**,与本机制引入前一致)。
Scopes []string
}
APIKeyPrincipal 认证所需的最小身份事实(user_api_keys × users 两表提炼, pkg 不直接引用 repo 模型类型)。
type APIKeyStore ¶
type APIKeyStore interface {
FindActiveByHash(ctx context.Context, keyHash string) (*APIKeyPrincipal, error)
TouchLastUsed(ctx context.Context, keyID uint, ip string) error
}
APIKeyStore API Key 认证所需持久化能力(bootstrap 注入 dao 桥实现)。 语义约定:Key 不存在/已吊销/已过期/属主非 active 状态统一返回 error, 调用方不区分原因(防枚举)。
type CtxKeyTraceID ¶
type CtxKeyTraceID = logger.CtxKeyTraceID
CtxKeyTraceID context.Context 中 trace_id 的键。
2026-10-16 起真相源下沉 pkg/logger(本包依赖 logger,反向 import 成环), 此处类型别名复用:类型同一性保证 WithValue/Value 配对,历史写入零迁移。
type IdentityRecord ¶
IdentityRecord 用户当前态快照(composition root 以 dao 桥注入)。
type LockedError ¶
type LockedError struct {
RemainingSeconds int
}
LockedError 被锁定错误(携带剩余秒数)
func (*LockedError) Error ¶
func (e *LockedError) Error() string
type Lockout ¶
type Lockout struct {
// contains filtered or unexported fields
}
Lockout 登录/取件失败计数锁定(防爆破,非 QPS 语义)。
维度由调用方决定(建议 "ip"、"user:xxx"、"code:xxx" 组合键):
- 窗口内失败次数达到 MaxAttempts → 锁定 LockSeconds
- Redis 可用时计数/锁定共享(多实例生效);否则进程内存兜底 (2026-10-18 起兜底托管给 oekit concurrency v0.12.0 ratelimit.Lockout ——concurrency#21 落地物;滑动窗口/锁定条目免空闲回收/容量 fail-open 均为其契约能力,替代手搓 memHit/memFail+周期 GC 约 80 行)
func GetDefaultLockout ¶
func GetDefaultLockout() *Lockout
GetDefaultLockout 获取全局锁定器(未初始化时用纯内存模式兜底,保证 handler 可用)
并发安全:先无锁读一次(快路径,绝大多数调用命中已初始化实例), 未初始化时再加写锁做 double-check 初始化。
func InitDefaultLockout ¶
InitDefaultLockout 初始化全局锁定器(bootstrap 调用一次)
兼作**测试重置**入口(传 nil 退回内存模式)⇒ 写操作受锁保护。
func NewLockout ¶
NewLockout 创建失败锁定器。rdb 为 nil 或配置未启用时仍返回可用实例(内存模式)。 nil 归一化:*redis.Client 的 typed-nil 赋给接口字段后 != nil 判真,会骗过 内存模式守卫并在方法调用时 panic——必须在注入边界处理。
func (*Lockout) CheckLocked ¶
CheckLocked 返回 (剩余锁定秒数, 是否被锁)。未启用恒返回 (0,false)。
func (*Lockout) RecordFailure ¶
RecordFailure 记录一次失败。返回 (是否触发锁定, 锁定秒数)。
type Metrics ¶
type Metrics struct {
// contains filtered or unexported fields
}
Metrics 指标容器。
func NewMetrics ¶
func NewMetrics() *Metrics
NewMetrics 创建并向默认注册表注册指标。 调用一次即可;重复调用会 panic(prometheus 已注册)。
type RateLimitConfig ¶
type RateLimitConfig struct {
GlobalQPS int // 全局 IP 维度 QPS(每 IP)
UploadQPS int // 上传接口 QPS
DownloadQPS int // 下载接口 QPS
LoginQPS int // 登录接口 QPS
Burst int // 突发容量
Enabled bool // 总开关
BlockSeconds int // 触发限流后的封禁秒数(0 = 不封禁)
UseRedis bool // Redis 固定窗口模式(多实例共享)
}
RateLimitConfig 限流配置(运行时可调)
func DefaultRateLimitConfig ¶
func DefaultRateLimitConfig() RateLimitConfig
DefaultRateLimitConfig 默认限流配置
func RateLimitConfigFromConf ¶
func RateLimitConfigFromConf() RateLimitConfig
RateLimitConfigFromConf 从全局配置读取(未配置段时用默认值)
type RateLimiter ¶
type RateLimiter struct {
// contains filtered or unexported fields
}
RateLimiter 限流管理器
func GetDefaultRateLimiter ¶
func GetDefaultRateLimiter() *RateLimiter
GetDefaultRateLimiter 获取默认限流器
func InitDefaultRateLimiter ¶
func InitDefaultRateLimiter(cfg RateLimitConfig) *RateLimiter
InitDefaultRateLimiter 初始化默认限流器
func (*RateLimiter) DownloadMiddleware ¶
func (rl *RateLimiter) DownloadMiddleware() app.HandlerFunc
DownloadMiddleware 下载接口限流
func (*RateLimiter) GlobalMiddleware ¶
func (rl *RateLimiter) GlobalMiddleware() app.HandlerFunc
GlobalMiddleware 全局限流中间件(用于所有接口)
func (*RateLimiter) LoginMiddleware ¶
func (rl *RateLimiter) LoginMiddleware() app.HandlerFunc
LoginMiddleware 登录接口限流
func (*RateLimiter) Middleware ¶
func (rl *RateLimiter) Middleware(s scope) app.HandlerFunc
Middleware 返回 Hertz 限流中间件
- scope: 限流维度(global / upload / download / login)
func (*RateLimiter) ProbeAllow ¶
func (rl *RateLimiter) ProbeAllow(ctx context.Context, s scope, probeIP string) bool
ProbeAllow 独立探测(不落内存 limiter 状态,admin /test 实测用)。 probeIP 以 "_test" 后缀隔离,避免污染真实客户端的限流计数。
func (*RateLimiter) SetRedis ¶
func (rl *RateLimiter) SetRedis(rdb *redis.Client)
SetRedis 注入 Redis(use_redis=true 时启用分布式固定窗口)
func (*RateLimiter) SnapshotConfig ¶
func (rl *RateLimiter) SnapshotConfig() RateLimitConfig
SnapshotConfig 返回当前配置副本(并发安全,admin 接口用)
func (*RateLimiter) Stats ¶
func (rl *RateLimiter) Stats() map[string]interface{}
Stats 返回限流器运行状态(/admin/ratelimit/status 用)
func (*RateLimiter) UpdateConfig ¶
func (rl *RateLimiter) UpdateConfig(cfg RateLimitConfig)
UpdateConfig 热更新配置
func (*RateLimiter) UploadMiddleware ¶
func (rl *RateLimiter) UploadMiddleware() app.HandlerFunc
UploadMiddleware 上传接口限流
type ScopeError ¶ added in v0.32.31
type ScopeError struct {
Scope string
}
ScopeError 权限不足(Key 有效,但缺该路径所需的能力面)。
与「Key 无效」区分开:前者是 403(Key 是好的、只是能力不够),后者是 401。 调用方据此能区分「换一把 Key」与「重新签发一把范围更宽的 Key」。
func (*ScopeError) Error ¶ added in v0.32.31
func (e *ScopeError) Error() string
type SecurityHeadersConfig ¶
type SecurityHeadersConfig struct {
// EnableHSTS 是否下发 Strict-Transport-Security。三态语义在 conf 层裁决
// (未配置=生产默认开/开发关),此处只接收最终布尔结果。
EnableHSTS bool
}
SecurityHeadersConfig 安全响应头中间件的配置项。
- X-Content-Type-Options: nosniff → 禁止浏览器嗅探 MIME 类型
- X-Frame-Options: SAMEORIGIN → 防止点击劫持(页面只能被同源 iframe 嵌入)
- X-XSS-Protection: 1; mode=block → 启用浏览器 XSS 过滤器(旧浏览器)
- Referrer-Policy: strict-origin-when-cross-origin → 限制 Referrer 泄露
- Strict-Transport-Security → 仅在生产(HTTPS)下启用 HSTS
- Cache-Control: no-store → 动态响应禁缓存(防中间盒子/浏览器缓存 泄露会话与私有数据);/assets/ 哈希命名构建产物豁免
另:剥离 Server 指纹头(hertz 在进入 handler 前已写入 "Server: hertz", 框架名帮助攻击者对号入座找现成漏洞利用,删除无副作用)。
可通过 SetSecurityHeadersConfig 调整。