File tree Expand file tree Collapse file tree 6 files changed +67
-5
lines changed
Expand file tree Collapse file tree 6 files changed +67
-5
lines changed Original file line number Diff line number Diff line change @@ -174,7 +174,7 @@ func (h *AuthHandler) UpdateToken(c *gin.Context) {
174174 Action : "token.rotate" ,
175175 ResourceType : "user" ,
176176 ResourceID : & userID ,
177- IP : c . ClientIP ( ),
177+ IP : utils . GetRealIP ( c . Request ),
178178 UserAgent : c .GetHeader ("User-Agent" ),
179179 Details : map [string ]interface {}{
180180 "role" : role ,
Original file line number Diff line number Diff line change @@ -223,7 +223,7 @@ func (h *LinkHandler) DeleteLink(c *gin.Context) {
223223 Action : "link.delete" ,
224224 ResourceType : "link" ,
225225 ResourceID : & linkID ,
226- IP : c . ClientIP ( ),
226+ IP : utils . GetRealIP ( c . Request ),
227227 UserAgent : c .GetHeader ("User-Agent" ),
228228 Details : map [string ]interface {}{
229229 "code" : code ,
Original file line number Diff line number Diff line change @@ -12,6 +12,7 @@ import (
1212
1313 "short-link/internal/repo"
1414 "short-link/internal/service"
15+ "short-link/utils"
1516
1617 "github.com/gin-gonic/gin"
1718)
@@ -37,7 +38,7 @@ func (h *RedirectHandler) Redirect(c *gin.Context) {
3738 ctx ,
3839 c .Request .Host ,
3940 code ,
40- c . ClientIP ( ),
41+ utils . GetRealIP ( c . Request ),
4142 c .GetHeader ("User-Agent" ),
4243 c .GetHeader ("Referer" ),
4344 )
Original file line number Diff line number Diff line change @@ -22,7 +22,7 @@ func LoggerMiddleware() gin.HandlerFunc {
2222
2323 latency := time .Since (start )
2424 status := c .Writer .Status ()
25- clientIP := c . ClientIP ( )
25+ clientIP := utils . GetRealIP ( c . Request )
2626
2727 utils .LogInfo ("[%s] %s %s %d %v %s" ,
2828 method ,
Original file line number Diff line number Diff line change @@ -7,6 +7,7 @@ package middleware
77import (
88 "net/http"
99 "short-link/cache"
10+ "short-link/utils"
1011 "time"
1112
1213 "github.com/gin-gonic/gin"
@@ -20,7 +21,8 @@ func RateLimitMiddleware() gin.HandlerFunc {
2021 return func (c * gin.Context ) {
2122 // 如果Redis可用,使用分布式限流
2223 if cache .RedisClient != nil {
23- key := "rate_limit:" + c .ClientIP ()
24+ realIP := utils .GetRealIP (c .Request )
25+ key := "rate_limit:" + realIP
2426 count , err := cache .RedisClient .Incr (cache .Ctx , key ).Result ()
2527 if err == nil {
2628 if count == 1 {
Original file line number Diff line number Diff line change 1+ /**
2+ * IP 工具
3+ * 处理代理链路的真实 IP 提取(X-Forwarded-For / X-Real-IP)
4+ * 实现 redo.md 2.5:代理链路真实 IP 处理
5+ */
6+ package utils
7+
8+ import (
9+ "net"
10+ "net/http"
11+ "strings"
12+ )
13+
14+ // GetRealIP 从请求中提取真实客户端 IP
15+ // 优先级:X-Forwarded-For(取第一个) > X-Real-IP > RemoteAddr
16+ // 同时进行基本验证,防止 IP 伪造
17+ func GetRealIP (r * http.Request ) string {
18+ // 1. 检查 X-Forwarded-For(可能包含多个 IP,用逗号分隔)
19+ forwardedFor := r .Header .Get ("X-Forwarded-For" )
20+ if forwardedFor != "" {
21+ // 取第一个 IP(最原始的客户端 IP)
22+ ips := strings .Split (forwardedFor , "," )
23+ if len (ips ) > 0 {
24+ ip := strings .TrimSpace (ips [0 ])
25+ if isValidIP (ip ) {
26+ return ip
27+ }
28+ }
29+ }
30+
31+ // 2. 检查 X-Real-IP
32+ realIP := r .Header .Get ("X-Real-IP" )
33+ if realIP != "" {
34+ ip := strings .TrimSpace (realIP )
35+ if isValidIP (ip ) {
36+ return ip
37+ }
38+ }
39+
40+ // 3. 回退到 RemoteAddr
41+ ip , _ , err := net .SplitHostPort (r .RemoteAddr )
42+ if err != nil {
43+ // 如果没有端口,直接使用 RemoteAddr
44+ return r .RemoteAddr
45+ }
46+ return ip
47+ }
48+
49+ // isValidIP 验证 IP 地址格式(基本验证,防止明显伪造)
50+ func isValidIP (ip string ) bool {
51+ parsed := net .ParseIP (ip )
52+ if parsed == nil {
53+ return false
54+ }
55+ // 拒绝内网 IP(如果配置了信任代理,可以放宽此限制)
56+ // 这里仅做格式验证,不拒绝内网 IP(因为可能确实来自内网代理)
57+ return true
58+ }
59+
You can’t perform that action at this time.
0 commit comments