242 lines
6.3 KiB
Go
242 lines
6.3 KiB
Go
|
|
package middleware
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"bytes"
|
|||
|
|
"io"
|
|||
|
|
"time"
|
|||
|
|
|
|||
|
|
"github.com/gin-gonic/gin"
|
|||
|
|
"github.com/google/uuid"
|
|||
|
|
"go.uber.org/zap"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// RequestLoggerMiddleware 请求日志中间件
|
|||
|
|
type RequestLoggerMiddleware struct {
|
|||
|
|
logger *zap.Logger
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewRequestLoggerMiddleware 创建请求日志中间件
|
|||
|
|
func NewRequestLoggerMiddleware(logger *zap.Logger) *RequestLoggerMiddleware {
|
|||
|
|
return &RequestLoggerMiddleware{
|
|||
|
|
logger: logger,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetName 返回中间件名称
|
|||
|
|
func (m *RequestLoggerMiddleware) GetName() string {
|
|||
|
|
return "request_logger"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetPriority 返回中间件优先级
|
|||
|
|
func (m *RequestLoggerMiddleware) GetPriority() int {
|
|||
|
|
return 80 // 中等优先级
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Handle 返回中间件处理函数
|
|||
|
|
func (m *RequestLoggerMiddleware) Handle() gin.HandlerFunc {
|
|||
|
|
return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
|
|||
|
|
// 使用zap logger记录请求信息
|
|||
|
|
m.logger.Info("HTTP Request",
|
|||
|
|
zap.String("client_ip", param.ClientIP),
|
|||
|
|
zap.String("method", param.Method),
|
|||
|
|
zap.String("path", param.Path),
|
|||
|
|
zap.String("protocol", param.Request.Proto),
|
|||
|
|
zap.Int("status_code", param.StatusCode),
|
|||
|
|
zap.Duration("latency", param.Latency),
|
|||
|
|
zap.String("user_agent", param.Request.UserAgent()),
|
|||
|
|
zap.Int("body_size", param.BodySize),
|
|||
|
|
zap.String("referer", param.Request.Referer()),
|
|||
|
|
zap.String("request_id", param.Request.Header.Get("X-Request-ID")),
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 返回空字符串,因为我们已经用zap记录了
|
|||
|
|
return ""
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// IsGlobal 是否为全局中间件
|
|||
|
|
func (m *RequestLoggerMiddleware) IsGlobal() bool {
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// RequestIDMiddleware 请求ID中间件
|
|||
|
|
type RequestIDMiddleware struct{}
|
|||
|
|
|
|||
|
|
// NewRequestIDMiddleware 创建请求ID中间件
|
|||
|
|
func NewRequestIDMiddleware() *RequestIDMiddleware {
|
|||
|
|
return &RequestIDMiddleware{}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetName 返回中间件名称
|
|||
|
|
func (m *RequestIDMiddleware) GetName() string {
|
|||
|
|
return "request_id"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetPriority 返回中间件优先级
|
|||
|
|
func (m *RequestIDMiddleware) GetPriority() int {
|
|||
|
|
return 95 // 最高优先级,第一个执行
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Handle 返回中间件处理函数
|
|||
|
|
func (m *RequestIDMiddleware) Handle() gin.HandlerFunc {
|
|||
|
|
return func(c *gin.Context) {
|
|||
|
|
// 获取或生成请求ID
|
|||
|
|
requestID := c.GetHeader("X-Request-ID")
|
|||
|
|
if requestID == "" {
|
|||
|
|
requestID = uuid.New().String()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置请求ID到上下文和响应头
|
|||
|
|
c.Set("request_id", requestID)
|
|||
|
|
c.Header("X-Request-ID", requestID)
|
|||
|
|
|
|||
|
|
// 添加到响应头,方便客户端追踪
|
|||
|
|
c.Writer.Header().Set("X-Request-ID", requestID)
|
|||
|
|
|
|||
|
|
c.Next()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// IsGlobal 是否为全局中间件
|
|||
|
|
func (m *RequestIDMiddleware) IsGlobal() bool {
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// SecurityHeadersMiddleware 安全头部中间件
|
|||
|
|
type SecurityHeadersMiddleware struct{}
|
|||
|
|
|
|||
|
|
// NewSecurityHeadersMiddleware 创建安全头部中间件
|
|||
|
|
func NewSecurityHeadersMiddleware() *SecurityHeadersMiddleware {
|
|||
|
|
return &SecurityHeadersMiddleware{}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetName 返回中间件名称
|
|||
|
|
func (m *SecurityHeadersMiddleware) GetName() string {
|
|||
|
|
return "security_headers"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetPriority 返回中间件优先级
|
|||
|
|
func (m *SecurityHeadersMiddleware) GetPriority() int {
|
|||
|
|
return 85 // 高优先级
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Handle 返回中间件处理函数
|
|||
|
|
func (m *SecurityHeadersMiddleware) Handle() gin.HandlerFunc {
|
|||
|
|
return func(c *gin.Context) {
|
|||
|
|
// 设置安全头部
|
|||
|
|
c.Header("X-Content-Type-Options", "nosniff")
|
|||
|
|
c.Header("X-Frame-Options", "DENY")
|
|||
|
|
c.Header("X-XSS-Protection", "1; mode=block")
|
|||
|
|
c.Header("Referrer-Policy", "strict-origin-when-cross-origin")
|
|||
|
|
c.Header("Content-Security-Policy", "default-src 'self'")
|
|||
|
|
c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
|
|||
|
|
|
|||
|
|
c.Next()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// IsGlobal 是否为全局中间件
|
|||
|
|
func (m *SecurityHeadersMiddleware) IsGlobal() bool {
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ResponseTimeMiddleware 响应时间中间件
|
|||
|
|
type ResponseTimeMiddleware struct{}
|
|||
|
|
|
|||
|
|
// NewResponseTimeMiddleware 创建响应时间中间件
|
|||
|
|
func NewResponseTimeMiddleware() *ResponseTimeMiddleware {
|
|||
|
|
return &ResponseTimeMiddleware{}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetName 返回中间件名称
|
|||
|
|
func (m *ResponseTimeMiddleware) GetName() string {
|
|||
|
|
return "response_time"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetPriority 返回中间件优先级
|
|||
|
|
func (m *ResponseTimeMiddleware) GetPriority() int {
|
|||
|
|
return 75 // 中等优先级
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Handle 返回中间件处理函数
|
|||
|
|
func (m *ResponseTimeMiddleware) Handle() gin.HandlerFunc {
|
|||
|
|
return func(c *gin.Context) {
|
|||
|
|
start := time.Now()
|
|||
|
|
|
|||
|
|
c.Next()
|
|||
|
|
|
|||
|
|
// 计算响应时间并添加到头部
|
|||
|
|
duration := time.Since(start)
|
|||
|
|
c.Header("X-Response-Time", duration.String())
|
|||
|
|
|
|||
|
|
// 记录到上下文中,供其他中间件使用
|
|||
|
|
c.Set("response_time", duration)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// IsGlobal 是否为全局中间件
|
|||
|
|
func (m *ResponseTimeMiddleware) IsGlobal() bool {
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// RequestBodyLoggerMiddleware 请求体日志中间件(用于调试)
|
|||
|
|
type RequestBodyLoggerMiddleware struct {
|
|||
|
|
logger *zap.Logger
|
|||
|
|
enable bool
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewRequestBodyLoggerMiddleware 创建请求体日志中间件
|
|||
|
|
func NewRequestBodyLoggerMiddleware(logger *zap.Logger, enable bool) *RequestBodyLoggerMiddleware {
|
|||
|
|
return &RequestBodyLoggerMiddleware{
|
|||
|
|
logger: logger,
|
|||
|
|
enable: enable,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetName 返回中间件名称
|
|||
|
|
func (m *RequestBodyLoggerMiddleware) GetName() string {
|
|||
|
|
return "request_body_logger"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetPriority 返回中间件优先级
|
|||
|
|
func (m *RequestBodyLoggerMiddleware) GetPriority() int {
|
|||
|
|
return 70 // 较低优先级
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Handle 返回中间件处理函数
|
|||
|
|
func (m *RequestBodyLoggerMiddleware) Handle() gin.HandlerFunc {
|
|||
|
|
if !m.enable {
|
|||
|
|
return func(c *gin.Context) {
|
|||
|
|
c.Next()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return func(c *gin.Context) {
|
|||
|
|
// 只记录POST, PUT, PATCH请求的body
|
|||
|
|
if c.Request.Method == "POST" || c.Request.Method == "PUT" || c.Request.Method == "PATCH" {
|
|||
|
|
if c.Request.Body != nil {
|
|||
|
|
bodyBytes, err := io.ReadAll(c.Request.Body)
|
|||
|
|
if err == nil {
|
|||
|
|
// 重新设置body供后续处理使用
|
|||
|
|
c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
|||
|
|
|
|||
|
|
// 记录请求体(注意:生产环境中应该谨慎记录敏感信息)
|
|||
|
|
m.logger.Debug("Request Body",
|
|||
|
|
zap.String("method", c.Request.Method),
|
|||
|
|
zap.String("path", c.Request.URL.Path),
|
|||
|
|
zap.String("body", string(bodyBytes)),
|
|||
|
|
zap.String("request_id", c.GetString("request_id")),
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
c.Next()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// IsGlobal 是否为全局中间件
|
|||
|
|
func (m *RequestBodyLoggerMiddleware) IsGlobal() bool {
|
|||
|
|
return false // 可选中间件,不是全局的
|
|||
|
|
}
|