一次nginx代理导致的登录异常问题
1、问题现象
业务系统迁移后,在访问业务系统时,登陆后会闪退,查看接口访问代码为 403

2、问题分析
查看业务代码,发现在登录时进行了token验证。
查看token生成规则,发现使用了 url 、 timestamp 及 用户信息进行校验,查看数据库,用户信息正确,只剩下了 url 以及时间戳的问题,检查服务器时间是同步的,最终定位到问题再url上。
服务整体架构是,通过前置nginx,代理到 k8s 的 loadbalancer 上,最终代理到服务容器中。
查看前置nginx配置如下:
map $http_x_real_ip $real_ip {
default $http_x_real_ip;
"" $remote_addr;
}
map $http_x_scheme $real_http_x_scheme {
default $http_x_scheme;
"" $scheme;
}
server {
listen 80;
server_name _;
keepalive_timeout 65;
client_max_body_size 1G;
client_body_buffer_size 128k;
proxy_connect_timeout 600;
proxy_read_timeout 600;
proxy_send_timeout 600;
proxy_hide_header Server;
sendfile on;
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;
index index.html;
add_header Access-Control-Allow-Origin *;
location ^~ / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Original-Request-URI $scheme://$http_host$request_uri;
proxy_http_version 1.1;
proxy_pass http://10.0.53.118:19020;
}
location ^~ /autodoc {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $real_ip;
proxy_set_header X-Scheme $real_http_x_scheme;
proxy_http_version 1.1;
proxy_pass http://10.0.53.118:19020/autodoc/;
}
location ^~ /calliper {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $real_ip;
proxy_set_header X-Scheme $real_http_x_scheme;
proxy_http_version 1.1;
proxy_pass http://10.0.53.118:19020/calliper/;
}
location ^~ /grater_v1 {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $real_ip;
proxy_set_header X-Scheme $real_http_x_scheme;
proxy_http_version 1.1;
proxy_pass http://10.0.53.118:19020/grater_v1/;
}
location ^~ /grater {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $real_ip;
proxy_set_header X-Scheme $real_http_x_scheme;
proxy_http_version 1.1;
proxy_pass http://10.0.53.118:19020/grater/;
}
}
可以看出,上面配置中 subpath 匹配规则中都是 /grater ,而代理规则中则是 http://10.0.53.118:19020/grater/ ,就会导致当访问 http://10.0.2.175:19020/grater/api/v1时,代理到后端的请求url为 http://10.0.53.118:19020/grater//api/v1。
3、解决方法
修改nginx配置文件如下:
map $http_x_real_ip $real_ip {
default $http_x_real_ip;
"" $remote_addr;
}
map $http_x_scheme $real_http_x_scheme {
default $http_x_scheme;
"" $scheme;
}
server {
listen 80;
server_name _;
keepalive_timeout 65;
client_max_body_size 1G;
client_body_buffer_size 128k;
proxy_connect_timeout 600;
proxy_read_timeout 600;
proxy_send_timeout 600;
proxy_hide_header Server;
sendfile on;
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;
index index.html;
add_header Access-Control-Allow-Origin *;
location ^~ / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Original-Request-URI $scheme://$http_host$request_uri;
proxy_http_version 1.1;
proxy_pass http://10.0.53.118:19020;
}
location ^~ /autodoc/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $real_ip;
proxy_set_header X-Scheme $real_http_x_scheme;
proxy_http_version 1.1;
proxy_pass http://10.0.53.118:19020/autodoc/;
}
location ^~ /calliper/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $real_ip;
proxy_set_header X-Scheme $real_http_x_scheme;
proxy_http_version 1.1;
proxy_pass http://10.0.53.118:19020/calliper/;
}
location ^~ /grater_v1/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $real_ip;
proxy_set_header X-Scheme $real_http_x_scheme;
proxy_http_version 1.1;
proxy_pass http://10.0.53.118:19020/grater_v1/;
}
location ^~ /grater/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $real_ip;
proxy_set_header X-Scheme $real_http_x_scheme;
proxy_http_version 1.1;
proxy_pass http://10.0.53.118:19020/grater/;
}
}
修改配置文件后再次进行访问,服务恢复正常。
2.4、总结
本次问题归根结底是由于nginx反向代理配置的问题,以下也记录一下反向代理配置的uri部分代理配置方法,方便查阅。
# nginx的部分代理(以下所有匹配规则均为 ^~ /uri/)
# 第一种配置:proxy_pass的端口号后面什么也不加
- 配置文件:proxy_pass http://192.168.99.100:8000;
- 客户端请求:http://www.abc.com/uri/ivvey.html
- 服务端转发:http://192.168.99.100:8000/uri/ivvey.html
'nginx的proxy_pass对于此种情况的处理方式是:将location中的uri传递给后端服务器,也就是当客户端访问http://www.abc.com/uri/iivey.html 时,会被反向代理到http://192.168.99.100:8000/uri/iivey.html 进行访问。
# 第二种配置:proxy_pass的端口号后跟/xxx/
- 配置文件:proxy_pass http://192.168.99.100:8000/xxx/;
- 客户端请求:http://www.abc.com/uri/ivvey.html
- 服务端转发:http://192.168.99.100:8000/xxx/ivvey.html
'nginx的proxy_pass对于此种情况的处理方式是:替换成proxy_pass指令中URL中含有的uri,也就是当客户端访问http://www.abc.com/uri/iivey.html 时,会被反向代理到http://192.168.99.100:8000/new_uri/iivey.html 进行访问。
# 第三种配置:proxy_pass的端口号后跟/
- 配置文件:proxy_pass http://192.168.99.100:8000/;
- 客户端请求:http://www.abc.com/uri/ivvey.html
- 服务端转发:http://192.168.99.100:8000/ivvey.html
'nginx的proxy_pass对于此种情况的处理方式是:替换成proxy_pass指令中URL中含有的uri,也就是当客户端访问http://www.abc.com/uri/iivey.html 时,会被反向代理到http://192.168.99.100:8000/iivey.html 进行访问。
这种反向代理方式其实是上面第二种uri代理方式的扩展,这里要重点注意下“proxy_pass http://192.168.99.100:8000/;” 这个url结尾有个"/"和没有"/"的区别。
评论区