• Netty获取真实IP


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

    Netty获取真实IP

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

    一、通过ChannelHandlerContext

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

    如果使用了代理,这时获取到的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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    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
    
    • 1

    解析结果为:

    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
    所以上面会换行
    
    • 1
    • 2
    • 3
    • 4

    解析代码

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

    50524f585920544350342031302e312e31312e36362031302e312e31312e36382035333732382038300d0a50524f585920544350342031302e312e352e32372031302e312e31312e36362036353431312038300d0a
    
    • 1

    解析出来的结果是:

    获取到数据PROXY TCP4 10.1.11.66 10.1.11.68 53728 80
    
    PROXY TCP4 10.1.5.27 10.1.11.66 65411 80
    
    
    • 1
    • 2
    • 3
    • 4

    解析代码

    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;
    		}
    	}
    }
    
    • 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
    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    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";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    java 各种架构图汇总
    云安全技术发展综述
    蓝桥杯算法双周赛心得——迷宫逃脱(记忆化搜索)
    [附源码]java毕业设计逸尘房屋销售管理系统
    SIndex: A Scalable Learned Index for String Keys
    MultiButton按键驱动模块的使用
    分享一个Redis自带的压测工具:redis-benchmark
    自适应零信任解决方案介绍
    DC/DC开关电源学习笔记(六)开关电源电路集成及封装工艺
    AI大预言模型——ChatGPT在地学、GIS、气象、农业、生态、环境等应用
  • 原文地址:https://blog.csdn.net/u010833154/article/details/125600156