Netty获取真实IP

本文参考:https://www.jianshu.com/p/cc8d592582c9 感谢分享

Netty获取真实IP

netty想要获取客户端请求的IP有两种方法:

一、通过ChannelHandlerContext

String ip=connContext.getClientIP().replace("/","").split(":")[0];

如果使用了代理,这时获取到的IP是代理服务器的IP

二、通过nginx获取真实IP

nginx对TCP协议进行代理需要引入stream模块,并修改nginx配置:

stream{
    upstream tcpstream{
        server  10.1.11.67:10001 weight=1;
    }
    server{
        listen 80;
        proxy_connect_timeout 20s;
        proxy_timeout 1m;
        # 开启代理
        proxy_protocol on;
        proxy_pass tcpstream;
    }
   error_log logs/error.log;
}

proxy protocol是HAProxy的作者Willy Tarreau于2010年开发和设计的一个Internet协议,通过为tcp添加一个很小的头信息,来方便的传递客户端信息(协议栈、源IP、目的IP、源端口、目的端口等),在网络情况复杂又需要获取用户真实IP时非常有用。其本质是在三次握手结束后由代理在连接中插入了一个携带了原始连接四元组信息的数据包。

  • 目前 proxy protocol有两个版本,v1仅支持human-readable报头格式(ASCIII码),v2需同时支持human-readable和二进制格式,即需要兼容v1格式
  • proxy protocol的接收端必须在接收到完整有效的 proxy protocol 头部后才能开始处理连接数据。因此对于服务器的同一个监听端口,不存在兼容带proxy protocol包的连接和不带proxy protocol包的连接。如果服务器接收到的第一个数据包不符合proxy protocol的格式,那么服务器会直接终止连接。
    在这里插入图片描述
    当三次握手成功后获取到的byte数据转成16进制为:
50524f58592054435034203132372e302e302e31203132372e302e302e312036303634322038300d0a

解析结果为:

PROXY TCP4 127.0.0.1 127.0.0.1 60642 80

注意结果为PROXY TCP4 127.0.0.1 127.0.0.1 60642 80\r\n
所以上面会换行

解析代码

以上是解析只有一层代理的,如果两层代理得到的数据有是什么样的呢?

50524f585920544350342031302e312e31312e36362031302e312e31312e36382035333732382038300d0a50524f585920544350342031302e312e352e32372031302e312e31312e36362036353431312038300d0a

解析出来的结果是:

获取到数据PROXY TCP4 10.1.11.66 10.1.11.68 53728 80

PROXY TCP4 10.1.5.27 10.1.11.66 65411 80

解析代码

public class MessageDecoder extends ByteToMessageDecoder {

	protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
		log.info("收到数据:{},{}",ctx.channel().id(),ByteBufUtil.hexDump(buffer));
		if(buffer.readableBytes() < ProtocolDataHead.LENGTH){
			return;
		}

		//protocol head
        ProtocolDataHead header = ProtocolDataHead.fromData(ByteBufUtil.getBytes(buffer,0,ProtocolDataHead.LENGTH));
        if(header.getStartMark().equals(ProtocolDataHead.STARTMARK)){
        	//本公司业务代码,不展示
        } else if(header.getStartMark().equals(NginxIPConst.NGINX_IP_TWO)){
        	if(buffer.readableBytes()<NginxIPConst.NGINX_IP_LENGTH){
        		return;
			}
			String readData=ByteBufUtil.hexDump(buffer);
			log.info("收到nginx消息:{}",ByteBufUtil.hexDump(buffer));
        	if(readData.indexOf(NginxIPConst.NGINX_IP_END)>0){
				byte[] nginxIP=new byte[readData.indexOf(NginxIPConst.NGINX_IP_END)/2+2];
				buffer.readBytes(nginxIP);
				NginxUtils.checkHead(ctx.channel().id().toString(),nginxIP);
				return;
			}else{
				buffer.clear();
			}
		}else{
			log.info("收到数据错误:{}",ByteBufUtil.hexDump(buffer));
			buffer.clear();
			return;
		}
	}
}
public class NginxUtils {
    private static ConcurrentHashMap<String,String> ipMap=new ConcurrentHashMap<>(1000);

    public static void checkHead(String channelId,byte[] bytes){
        String nginxIP=new String(bytes, Charset.forName("UTF-8"));
        log.info("获取到数据{}",nginxIP);
        String[] strs=nginxIP.split(" ");
        if(strs.length>2){
            ipMap.put(channelId,strs[2]);
        }
    }

    public static String getIp(String channelId){
        return ipMap.get(channelId);
    }

    public static String removeIp(String channelId){
        return ipMap.remove(channelId);
    }
}
public class NginxIPConst {

    //nginx透传ip最小长度
    public static final int NGINX_IP_LENGTH = 41;

    //nginx透传ip前两位数据
    public static final String NGINX_IP_TWO = "PR";

    //nginx结尾符
    public static final String NGINX_IP_END = "0d0a";
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值