美文网首页
spring cloud gateway中获取客户端真实请求IP

spring cloud gateway中获取客户端真实请求IP

作者: terry蒋 | 来源:发表于2020-05-15 15:55 被阅读0次

客户端请求到服务端时,可能会被 HTTP 代理、负载均衡等转发服务进行处理,再转发,如果要获取最原始的客户端真实IP,需要根据常用的协议,进行获取。
参考:HTTP 请求头中的 X-Forwarded-For https://www.jianshu.com/p/15f3498a7fad
SpringBoot获取请求的IP地址 https://www.jianshu.com/p/3871a2c47c09

如何获取

1.spring cloud gateway中获取客户端真实ip的方式和普通java web应用中获取的方式类似,即从request中先从代理转发的相关请求头中获取原始客户端ip,如果获取不到,则取当前与服务器通信的客户端对应的ip

2.示例代码
(1)创建获取IP的工具类

/**
* IP工具类
* @date 20-5-15 10:03
*/
public class IpUtils {

    private static final  LogTool LOGGER = LogTool.getLogger(IpUtils.class);

    private static final String UNKNOWN = "unknown";
    private static final String LOCALHOST = "127.0.0.1";
    private static final String SEPARATOR = ",";

    private static final String HEADER_X_FORWARDED_FOR = "x-forwarded-for";
    private static final String HEADER_PROXY_CLIENT_IP = "Proxy-Client-IP";
    private static final String HEADER_WL_PROXY_CLIENT_IP = "WL-Proxy-Client-IP";

    /**
     * 获取真实客户端IP
     * @param serverHttpRequest
     * @return
     */
    public static String getRealIpAddress(ServerHttpRequest serverHttpRequest) {
        String ipAddress;
        try {
            // 1.根据常见的代理服务器转发的请求ip存放协议,从请求头获取原始请求ip。值类似于203.98.182.163, 203.98.182.163
            ipAddress = serverHttpRequest.getHeaders().getFirst(HEADER_X_FORWARDED_FOR);
            if (StringUtil.isTrimEmpty(ipAddress) || UNKNOWN.equalsIgnoreCase(ipAddress)) {
                ipAddress = serverHttpRequest.getHeaders().getFirst(HEADER_PROXY_CLIENT_IP);
            }
            if (StringUtil.isTrimEmpty(ipAddress) || UNKNOWN.equalsIgnoreCase(ipAddress)) {
                ipAddress = serverHttpRequest.getHeaders().getFirst(HEADER_WL_PROXY_CLIENT_IP);
            }


            // 2.如果没有转发的ip,则取当前通信的请求端的ip
            if (StringUtil.isTrimEmpty(ipAddress) || UNKNOWN.equalsIgnoreCase(ipAddress)) {
                InetSocketAddress inetSocketAddress = serverHttpRequest.getRemoteAddress();
                if(inetSocketAddress != null) {
                    ipAddress = inetSocketAddress.getAddress().getHostAddress();
                }
                // 如果是127.0.0.1,则取本地真实ip
                if (LOCALHOST.equals(ipAddress)) {
                    InetAddress localAddress = StringUtil.getLocalHostLANAddress();
                    if(localAddress.getHostAddress() != null) {
                        ipAddress = localAddress.getHostAddress();
                    }
                }
            }


            // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            // "***.***.***.***"
            if (ipAddress != null) {
                ipAddress = ipAddress.split(SEPARATOR)[0].trim();
            }
        } catch (Exception e) {
            LOGGER.error("解析请求IP失败", e);
            ipAddress = "";
        }
        return ipAddress == null ? "" : ipAddress;
    }
}

(2)使用GlobalFilter过滤器拦截

@Component
public class AccessInFilter implements GlobalFilter, Ordered {

    private static LogTool logger = LogTool.getLogger(AccessInFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        String ip = IpUtils.getRealIpAddress(exchange.getRequest());
        // 该步骤可选。可以传递给下游服务器,用于业务处理
        ServerHttpRequest request = exchange.getRequest().mutate()
                .header(GatewayConstant.REQUEST_HEADER_KEY_CLIENT_REAL_IP, new String[]{ip})
                .build();

        logger.info("访问入口过滤器AccessInFilter.";
        return chain.filter(exchange.mutate().request(request).build());
    }


    @Override
    public int getOrder() {
        return -3;
    }
}

注意:

1.上面的方法其实也未必一定能获取到真实IP,因为代理服务可能没有按常用的协议进行处理,比如,没有采用x-forwarded-for这些请求头对原始ip进行透传,或者删除了这些请求头。那原始的ip信息将会丢失。
2.没有采用spring cloud gateway封装的方式

 RemoteAddressResolver resolver = XForwardedRemoteAddressResolver.trustAll();
InetAddress inetAddress = resolver.resolve(exchange).getAddress();
String ip = inetAddress == null ? "" : resolver.resolve(exchange).getAddress().getHostAddress();

因为该方式代码有缺陷,查看源码逻辑:
org.springframework.cloud.gateway.support.ipresolver.XForwardedRemoteAddressResolver#extractXForwardedValues

private List<String> extractXForwardedValues(ServerWebExchange exchange) {
    List<String> xForwardedValues = exchange.getRequest().getHeaders()
            .get(X_FORWARDED_FOR);
    if (xForwardedValues == null || xForwardedValues.isEmpty()) {
        return Collections.emptyList();
    }
    if (xForwardedValues.size() > 1) {
        log.warn("Multiple X-Forwarded-For headers found, discarding all");
        return Collections.emptyList();
    }
    //  此处有问题,写死了是逗号加空格进行分割,但是有时只有逗号,不存在空格,这种情况,会导致解析失败,返回的空的ip
    List<String> values = Arrays.asList(xForwardedValues.get(0).split(", "));
    if (values.size() == 1 && !StringUtils.hasText(values.get(0))) {
        return Collections.emptyList();
    }
    return values;
}

相关文章

网友评论

      本文标题:spring cloud gateway中获取客户端真实请求IP

      本文链接:https://www.haomeiwen.com/subject/xwvcohtx.html