后端获取用户真实IP

场景:

用户访问-----haproxy(docker部署)-----Nginx前端(docker部署)-----K8S后端
使用K8S部署的后端需要获取用户访问的IP地址(用户使用终端的IP)。

解决方案

用户访问时,在Haproxy处添加添加一个自定义的X-Real-IP请求头,后端通过这个请求头获取用户真实IP。

HTTP

只需要配置Haproxy即可,不用修改Nginx配置,Haproxy配置示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
global
log 127.0.0.1 local0
daemon
defaults
log global
mode tcp
option abortonclose
option redispatch
option forwardfor except 127.0.0.0/8
retries 3
maxconn 2000
timeout connect 5000
timeout client 50000
timeout server 50000
frontend admin_stats
bind :8033
mode http
stats enable
option httplog
maxconn 10
stats refresh 30s
stats uri /admin
stats auth admin:123123
stats hide-version
stats admin if TRUE
frontend header_front
bind *:32297
mode http
option forwardfor
acl android-browser hdr_reg(User-Agent) -i Android
use_backend app_backend if android-browser
default_backend pc_backend
backend app_backend
mode http
server mom 172.16.30.52:32298 check
http-request set-header X-Real-IP %[src]
backend pc_backend
mode http
server mom-app 172.16.30.52:32298
http-request set-header X-Real-IP %[src]

haproxy 配置中添加 option forwardforhttp-request set-header X-Real-IP %[src]

HTTPS

如果使用HTTPS访问,那么需要同时修改Haproxy配置及Nginx配置。
Haproxy配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

global
log 127.0.0.1 local0
daemon
defaults
log global
mode tcp
option abortonclose
option redispatch
#option forwardfor except 127.0.0.0/8
retries 3
maxconn 2000
timeout connect 5000
timeout client 50000
timeout server 50000
frontend admin_stats
bind :8033
mode http
stats enable
option httplog
maxconn 10
stats refresh 30s
stats uri /admin
stats auth admin:123123
stats hide-version
stats admin if TRUE
frontend header_front
bind *:32295
mode http
option forwardfor
acl android-browser hdr_reg(User-Agent) -i Android
use_backend app_backend if android-browser
default_backend pc_backend
frontend https_frontend
bind *:32297
mode tcp
acl tls req.ssl_hello_type 1
tcp-request inspect-delay 5s
tcp-request content accept if tls
default_backend pc_https_backend
backend app_backend
mode http
server mom 172.16.30.52:32298 check
http-request set-header X-Real-IP %[src]
backend pc_backend
mode http
server mom-app 172.16.30.52:32298
http-request set-header X-Real-IP %[src]
backend pc_https_backend
mode tcp
server mom-https-app 172.16.30.52:32296 send-proxy-v2

使用mode tcp以及send-proxy-v2进行透传。

Nginx配置示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

#user nobody;
worker_processes 1;
error_log /var/log/nginx/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;

events {
worker_connections 1024;
}

http {
include mime.types;
default_type application/octet-stream;
log_format main '$proxy_protocol_addr - $remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/logs/nginx/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 443 ssl proxy_protocol;
server_name webServer;
ssl_certificate /home/cert/6030147_wx.xxx.com.pem;
ssl_certificate_key /home/cert/6030147_wx.xxx.com.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!NULL:!aNULL:!ADH:!3DES:!RC4;
ssl_prefer_server_ciphers on;
autoindex_localtime on;
client_max_body_size 1200M;
add_header X-Content-Type-Options nosniff;
add_header Strict-Transport-Security "max-age=315360; includeSubdomains;";
add_header X-XSS-Protection "1; mode=block";
location / {
root html;
index index.html index.htm;
}
location ^~/api/ {
proxy_read_timeout 300s;
proxy_pass http://172.16.30.52:32299/api/;
proxy_set_header X-Real-IP $proxy_protocol_addr;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}

nginx.conf 中添加 proxy_protocol以及proxy_set_header X-Real-IP $proxy_protocol_addr;

后端代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
@RequestMapping("/api")
public class IpController {
@GetMapping("/getRealIp")
public String getRealIp(HttpServletRequest request) {
// 获取X-Real-IP请求头
String realIp = request.getHeader("X-Real-IP");
// 打印到控制台
System.out.println("X-Real-IP: " + realIp);
// 返回响应
return "X-Real-IP: " + realIp;
}
}