依据 coord-final-decisions §3.8 W1-W8 裁决与 president-final-rulings §2.15/§2.16/§2.19 完整实现网关硬化: - W1/W2: 错误码 GW_ 前缀 + ActionState 信封响应体 - W3: 全量替换为 log/slog 结构化日志 - W4: /readyz 并行 ping 9 下游 + 软失败规则 - W5: 7 个业务 Prometheus 指标 + /metrics 端点 - W6: tracer 资源属性补全(name/version/env/host) - W7: DevMode=true && ENV=production panic 防护 - W8: 保持共享 downstream 熔断 P2 RS256 升级:接入 shared-go/jwks.Fetcher(TTL 5min)。 P2.7+P3-P5 路由扩展:student/parent/messages/dashboard。 文档同步:README/01/02/known-issues,arch.db 已更新。 质量校验:go vet + build + test 均通过。
50 lines
1.7 KiB
Go
50 lines
1.7 KiB
Go
package middleware
|
||
|
||
import (
|
||
"net/http"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
// SecurityHeaders 设置安全响应头中间件。
|
||
// 设置:X-Content-Type-Options: nosniff
|
||
//
|
||
// X-Frame-Options: DENY
|
||
// Referrer-Policy: strict-origin-when-cross-origin
|
||
// Strict-Transport-Security: max-age=31536000; includeSubDomains
|
||
// Content-Security-Policy: default-src 'self'
|
||
// 移除 Server 头
|
||
func SecurityHeaders() gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
h := c.Writer.Header()
|
||
h.Set("X-Content-Type-Options", "nosniff")
|
||
h.Set("X-Frame-Options", "DENY")
|
||
h.Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
||
h.Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
|
||
h.Set("Content-Security-Policy", "default-src 'self'")
|
||
h.Del("Server")
|
||
c.Next()
|
||
// 响应处理结束后再次移除 Server 头,防止处理过程中被写入
|
||
c.Writer.Header().Del("Server")
|
||
}
|
||
}
|
||
|
||
// RequestBodyLimit 限制请求体大小中间件。
|
||
// 超限时返回 413 + ActionState 信封(W1/W2 裁决):
|
||
//
|
||
// {"success":false,"error":{"code":"GW_REQUEST_TOO_LARGE","message":"..."}}
|
||
//
|
||
// 通过 Content-Length 预检 + http.MaxBytesReader 双重保障:
|
||
// - Content-Length 预检:已知大小的请求体立即拒绝(JSON 信封响应)
|
||
// - MaxBytesReader:流式请求体(无 Content-Length)读取超限时由标准库返回 413
|
||
func RequestBodyLimit(maxBytes int64) gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
if c.Request.ContentLength > maxBytes {
|
||
abortGW(c, http.StatusRequestEntityTooLarge, "GW_REQUEST_TOO_LARGE", "request body too large")
|
||
return
|
||
}
|
||
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, maxBytes)
|
||
c.Next()
|
||
}
|
||
}
|