关于使用代理后,后端获取客户端IP中遇到的问题(比如.NetCore、Vue、Nginx)

  首先,我们有多种方式获取本地IP地址,比如:.net core获取本地Ip地址的方法 ,这种方式与项目类型无关。

  如果后端项目是一个web项目,我们还可以通过HttpContext来获取后端项目所在服务器的本地IP,而且还能获取客户端的IP地址:  

    var connection = HttpContext.Connection;

    //客户端IP地址(v4和v6)
    var remoteIpAddressIPv4 = connection.RemoteIpAddress?.MapToIPv4()?.ToString();
    var remoteIpAddressIPv6 = connection.RemoteIpAddress?.MapToIPv6()?.ToString();
    //服务端本地IP地址(v4和v6)
    var localIpAddressIPv4 = connection.LocalIpAddress?.MapToIPv4()?.ToString();
    var localIpAddressIPv6 = connection.LocalIpAddress?.MapToIPv6()?.ToString();

  但是现在的web项目应该很少有直接面向客户端使用的吧,都是中间或多或少有几个网关或者代理,比如使用nginx:  

    location / {        
        proxy_pass http://localhost:5000;
    }

  或者前端Vue开发时使用了使用http-proxy-middleware做代理:  

  devServer: {
    proxy: {
      '/api': {
        target: process.env.VUE_APP_API_BASE_URL,
        ws: false,
        changeOrigin: true,
        pathRewrite: {
          '^/api': '' // 需要rewrite的,
        }
      }
    }
  }

  那么问题来了,一旦客户端的请求被代理了,那么上面获取获取客户端IP地址的方式就不准确了,因为请求被代理转发了,这样它获取的就是代理端的IP,而不是客户端的IP,那么怎么解决这个问题呢?

  这就要说到两个特殊的请求头了:X-Real-IP 与 X-Forwarded-For

  X-Real-IP:表示真实的IP,但是实际上,它表示多级代理中最后一个代理的IP,可惜的是,它只是被一些组织机构认可,貌似并没有一个标准的规范。

  X-Forwarded-For:表示请求从客户端发起后所经过的所有代理的IP地址,这些IP地址使用英文逗号+空格(, )进行分隔,其中第一个就是客户端的IP地址,形如:  

    X-Forwarded-For: 192.168.128.111, 192.168.128.112, 192.168.128.113

  那么解决这个问题的关键就是在代理转发请求时,我们需要给转发的请求加上这个X-Forwarded-For请求头!X-Real-IP可以添加,也可以不添加,只是后端读取不一样。

  如果使用的是Nginx:  

    location / {
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # 如果是代理websocket,还需要下面两个
        # proxy_set_header Upgrade $http_upgrade;
        # proxy_set_header Connection "upgrade";
    }

  $remote_addr表示上一级代理的IP,如果上一级是客户端,那么它就表示客户端的IP,$proxy_add_x_forwarded_for表示将$remote_addr追加到上一级代理的X-Forwarded-For请求头中,并将追加后的内容作为X-Forwarded-For请求头转发到下一级,这样客户端的IP就一级一级的转发到了服务端

  如果是Vue开发过程中使用了http-proxy-middleware做代理:  

  devServer: {
    proxy: {
      '/api': { //如果是API接口
        target: process.env.VUE_APP_API_BASE_URL,
        ws: false,
        changeOrigin: true,
        pathRewrite: {
          '^/api': '' // 需要rewrite的,
        },
        onProxyReq(proxyReq, req, res) {
          //req中默认是携带有x-forwarded-for请求头的,只需添加X-Real-IP请求头
          req.headers['X-Real-IP'] = req.ip
        }
      },
      '/socket': { //如果是websocket
        target: process.env.VUE_APP_SOCKET_BASE_URL,
        ws: true,
        changeOrigin: true,
        onProxyReqWs(proxyReq, req, socket, options, head) {
          //req中默认是携带有x-forwarded-for请求头的,只需添加X-Real-IP请求头
          req.headers['X-Real-IP'] = req.ip
        }
      }
    }
  }

  这里onProxyReq是代理普通http接口前会触发的事件,onProxyReqWs则是代理websocket前会触发的事件

  接着后端获取IP可以从下面的规则中获取:  

    string ip = HttpContext.Request.Headers["X-Real-IP"].ToString();
    if (string.IsNullOrEmpty(ip))
    {
        string ips = HttpContext.Request.Headers["X-Forwarded-For"].ToString();
        ip = ips.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
        if (string.IsNullOrEmpty(ip))
        {
            ip = HttpContext.Connection.RemoteIpAddress?.MapToIPv4()?.ToString();
        }
    }

 

posted @ 2022-02-21 10:43  没有星星的夏季  阅读(2136)  评论(0编辑  收藏  举报