故事
首页
指南
  • Java
  • Python
  • Linux
  • 前端
  • Docker
  • 实践
  • 折腾
  • 分类
  • 标签
  • 归档
壁纸 (opens new window)
GitHub (opens new window)
首页
指南
  • Java
  • Python
  • Linux
  • 前端
  • Docker
  • 实践
  • 折腾
  • 分类
  • 标签
  • 归档
壁纸 (opens new window)
GitHub (opens new window)
  • Java基础

    • String类的深入学习
    • HashMap源码学习
    • Java8新特性回顾
  • Java框架

    • POI事件模式解析并读取Excel文件数据
    • SpringMVC的执行流程源码分析
    • netty+websocket实现即时通讯功能
    • 分布式定时任务解决方案xxl-job
    • spring-session实现集群session共享
    • springcloud优雅下线服务
    • Java日志发展历史
    • Mybatis Generator配置
    • JavaSPI机制和Springboot自动装配原理
    • Spring Cloud Config接入
    • 使用spring validation进行参数校验
    • dubbo接口超时配置
    • ConfigurationProperties注解
    • EasyExcel导出excel
  • 中间件

    • 分布式锁解决方案
    • 关于消息中间件MQ
    • kafka学习记录
    • kakfa实践
    • kakfa重复消费问题处理
  • 数据库

    • MySql索引
    • SQL优化学习
    • Otter实现数据全量增量同步
  • 开发工具

    • Maven的生命周期
    • nexus无法下载依赖的问题记录
  • 其他

    • linux服务器安装OpenOffice踩坑
    • POI踩坑-zip file is closed
    • OpenJDK没有jstack等命令的解决办法
    • 微信小程序加密数据对称解密工具类
    • mybatis的classpath配置导致的jar包读取问题
    • feign请求导致的用户ip获取问题记录
      • 背景
      • 排查
    • ribbon刷新服务列表间隔和canal的坑
    • cpu占用率高排查思路
  • 简介
  • java
  • 其他
storyxc
2022-04-19

feign请求导致的用户ip获取问题记录

# feign请求导致的用户ip获取问题记录

# 背景

有个功能需要做ip的判断,区分请求是来自用户端还是来自其他服务的feign调用,其中有个一个服务发起的feign调用,一直无法获取到真实的服务器ip,而一直是用户端的ip。

# 排查

  1. 首先贴出来获取ip的工具类
public static String getIpAddr(HttpServletRequest request) {
        String ipAddress = request.getHeader("x-forwarded-for");
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
                //根据网卡取本机配置的IP
                InetAddress inet = null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ipAddress = inet.getHostAddress();
            }
        }
        //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
        if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15
            if (ipAddress.indexOf(",") > 0) {
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }
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
  1. 根据调试发现,请求的``x-forwarded-for`请求头是一直包含两个ip的,第一个ip是用户端的ip,第二个是nginx所在的服务器ip,这个也好理解,通过代理转发的请求,会把代理ip拼在真实ip后面。但是问题是这个请求是由其他服务feign发起的,理应不存在nginx转发(通过注册中心的服务名调用,绕过nginx)。

HTTP 请求头中的 X-Forwarded-For (opens new window)

Nginx remote_addr和proxy_add_x_forwarded_for变量详解 (opens new window)]

  1. 把问题转到排查feign调用上后发现,该服务实例化了feign.builder来实现传递自定义请求头的目标
@Bean
	public Feign.Builder feignBuilder() {
		return Feign.builder().requestInterceptor(new RequestInterceptor() {
			@Override
			public void apply(RequestTemplate requestTemplate) {
				Map<String, String> customHeaders = WebUtils.getCustomHeaders();
				customHeaders.forEach((k, v) -> {
					requestTemplate.header(k, v);
				});
			}
		});
	}

---
  
public static Map<String, String> getCustomHeaders() {
        Map<String, String> headers = new HashMap();
        HttpServletRequest request = RequestContextHelper.getRequest();
        Enumeration<String> headerNames = request.getHeaderNames();

        while(headerNames.hasMoreElements()) {
            String headerName = ((String)headerNames.nextElement()).toLowerCase();
            if (headerName.startsWith("x-")) {
                String headerValue = request.getHeader(headerName);
                if (headerValue != null) {
                    headers.put(headerName, headerValue);
                }
            }
        }

        headers.put("x-invoker-ip", IpUtils.getLocalIpAddr());
        if (!headers.containsKey("x-auth-token")) {
            headers.put("x-auth-token", TokenGenerator.generateWithSign());
        }

        return headers;
    }
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

至此,问题解决,x-forwarded-for被放在了feign请求头中,导致了上述问题,这里把x-forwarded-for请求头排除即可。

  1. 分析:

    请求流程: 用户->nginx->网关服务->a服务->feign调用->b服务

用户的请求通过nignx后代理ip就会被添加到x-forwarded-for请求头中,此时x-forwarded-for请求头为:用户ip,nginx服务器的ip,后请求从网关路由到a服务,a服务通过feign.buider的添加拦截器方法,增加了一个添加指定请求头到feign的请求中的拦截器,导致原来的x-forwarded-for被原封不动的传到了b服务,b服务根据这个请求头获取ip时,就拿到了原始用户的ip。

编辑 (opens new window)
#踩坑#feign
上次更新: 2022/04/21, 18:12:24
mybatis的classpath配置导致的jar包读取问题
ribbon刷新服务列表间隔和canal的坑

← mybatis的classpath配置导致的jar包读取问题 ribbon刷新服务列表间隔和canal的坑→

Theme by Vdoing | Copyright © 2019-2023 story | 豫ICP备19046036号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式