初始提交:OCI 面板后端(含 GenAI 网关一期)
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"oci-portal/internal/service"
|
||||
)
|
||||
|
||||
// ctxRealIPKey 是真实IP中间件写入 gin.Context 的键。
|
||||
const ctxRealIPKey = "realIP"
|
||||
|
||||
// RealIPMiddleware 按安全设置解析客户端真实 IP 写入上下文,
|
||||
// 全链路(IP 限速 / 登录守卫 / 系统日志留痕)统一消费;gin 内置
|
||||
// TrustedProxies 已禁用,信任边界完全由该配置决定。
|
||||
func RealIPMiddleware(settings *service.SettingService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Set(ctxRealIPKey, resolveRealIP(c.Request, settings.SecurityCached().RealIPHeader))
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// requestIP 取中间件解析的真实 IP;未经中间件(单测直调 handler)时回退 RemoteAddr。
|
||||
func requestIP(c *gin.Context) string {
|
||||
if ip := c.GetString(ctxRealIPKey); ip != "" {
|
||||
return ip
|
||||
}
|
||||
return remoteHost(c.Request)
|
||||
}
|
||||
|
||||
// resolveRealIP 依配置头解析:X-Forwarded-For 取链尾(直连反代的对端,
|
||||
// 反代会把对端追加到链尾,链首可被客户端伪造);X-Real-IP 等单值头直取;
|
||||
// 头缺失或未配置(直连部署)回退 RemoteAddr。
|
||||
func resolveRealIP(r *http.Request, header string) string {
|
||||
switch header {
|
||||
case "":
|
||||
case "X-Forwarded-For":
|
||||
if v := r.Header.Get(header); v != "" {
|
||||
parts := strings.Split(v, ",")
|
||||
if ip := strings.TrimSpace(parts[len(parts)-1]); ip != "" {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
default:
|
||||
if ip := strings.TrimSpace(r.Header.Get(header)); ip != "" {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
return remoteHost(r)
|
||||
}
|
||||
|
||||
// remoteHost 取 RemoteAddr 的 host 部分。
|
||||
func remoteHost(r *http.Request) string {
|
||||
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
return r.RemoteAddr
|
||||
}
|
||||
return host
|
||||
}
|
||||
Reference in New Issue
Block a user