当前位置: 移动技术网 > 网络运营>服务器>nginx > 基于Nginx 反向代理获取真实IP的问题详解

基于Nginx 反向代理获取真实IP的问题详解

2019年04月18日  | 移动技术网网络运营  | 我要评论

一、前言

前文nginx 解决webapi跨域二次请求以及vue单页面问题 当中虽然解决了跨域问题带来的二次请求,但也产生了一个新的问题,就是如果需要获取用户ip的时候,获取的ip地址总是本机地址。

二、原因

由于nginx反向代理后,在应用中取得的ip都是反向代理服务器的ip,取得的域名也是反向代理配置的url的域名。

三、解决方案

解决该问题,需要在nginx反向代理配置中添加一些配置信息,目的将客户端的真实ip和域名传递到应用程序中。同时,也要修改获取ip地址的方法。

但是需要注意的是,通过nginx反向代理后,如果访问ip通过了几层代理,可能取得的ip地址是这种格式:clientip,proxy1,proxy2。

如果需要将ip地址插入到数据库的话,需要做防止注入。因此要对上述的ip地址的格式进行截取。

3.1 nginx 配置如下

server {
  listen  9461; # 监听端口号
  server_name localhost 192.168.88.22; # 访问地址
  location / {
   root 项目路径; # 例如:e:/publish/xxx/;
   index ;
   
   # 此处用于处理 vue、angular、react 使用h5 的 history时 重写的问题
   if (!-e $request_filename) {
    rewrite ^(.*) / last;
    break;
   }
  }
  
  # 代理服务端接口
  location /api {
   proxy_pass http://localhost:9460/api;# 代理接口地址
   # host配置以及域名传递
   proxy_set_header host $host;
   proxy_set_header x-real-ip $remote_addr;
   proxy_set_header remote-host $remote_addr;
   proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for;
  }
 }

3.2 c#代码获取真实ip方法

#region ip(客户端ip地址)
/// <summary>
/// 客户端ip地址
/// </summary>
public static string ip
{
 get
 {
  var result = string.empty;
  if (httpcontext.current != null)
  {
   result = getwebclientip();
  }
  if (string.isnullorwhitespace(result))
  {
   result = getlanip();
  }
  return result;
 }
}
/// <summary>
/// 获取web客户端的ip
/// </summary>
/// <returns></returns>
private static string getwebclientip()
{
 var ip = getwebproxyrealip() ?? getwebremoteip();
 foreach (var hostaddress in dns.gethostaddresses(ip))
 {
  if (hostaddress.addressfamily == addressfamily.internetwork)
  {
   return hostaddress.tostring();
  }
 }
 return string.empty;
}
/// <summary>
/// 获取web远程ip
/// </summary>
/// <returns></returns>
private static string getwebremoteip()
{
 try
 {
  return httpcontext.current.request.servervariables["http_x_forwarded_for"] ??
    httpcontext.current.request.servervariables["remote_addr"] ?? "";
 }
 catch (exception e)
 {
  return string.empty;
 }
}
/// <summary>
/// 获取web代理真实ip
/// </summary>
/// <returns></returns>
private static string getwebproxyrealip()
{
 var request = httpcontext.current.request;
 string ip = request.headers.get("x-forwarded-for");
 if (string.isnullorempty(ip) || string.equals("unknown", ip, stringcomparison.ordinalignorecase))
 {
  ip = request.headers.get("proxy-client-ip");
 }
 if (string.isnullorempty(ip) || string.equals("unknown", ip, stringcomparison.ordinalignorecase))
 {
  ip = request.headers.get("wl-proxy-client-ip");
 }
 if (string.isnullorempty(ip) || string.equals("unknown", ip, stringcomparison.ordinalignorecase))
 {
  ip = request.userhostaddress;
 }
 if (string.isnullorempty(ip))
 {
  return string.empty;
 }
 // 可能存在如下格式:x-forwarded-for: client, proxy1, proxy2
 if (ip.contains(", "))
 {
  // 如果存在多个反向代理,获得的ip是一个用逗号分隔的ip集合,取第一个
  // x-forwarded-for: client 第一个
  string[] ips = ip.split(new string[1] {", "}, stringsplitoptions.removeemptyentries);
  var i = 0;
  for (i = 0; i < ips.length; i++)
  {
   if (ips[i] != "")
   {
    // 判断是否为内网ip
    if (false == isinnerip(ips[i]))
    {
     ipaddress realip;
     if (ipaddress.tryparse(ips[i], out realip) && ips[i].split('.').length == 4)
     {
      //合法ip
      return ips[i];
     }
     return "";
    }
   }
  }
  ip = ips[0];// 默认获取第一个ip地址
 }
 return ip;
}
/// <summary>
/// 判断ip地址是否为内网ip地址
/// </summary>
/// <param name="ip">ip地址</param>
/// <returns></returns>
private static bool isinnerip(string ip)
{
 bool isinnerip = false;
 ulong ipnum = ip2ulong(ip);
 /**
  * 私有ip
  * a类:10.0.0.0-10.255.255.255
  * b类:172.16.0.0-172.31.255.255
  * c类:192.168.0.0-192.168.255.255
  * 当然,还有127这个网段是环回地址
  */
 ulong abegin = ip2ulong("10.0.0.0");
 ulong aend = ip2ulong("10.255.255.255");
 ulong bbegin = ip2ulong("172.16.0.0");
 ulong bend = ip2ulong("10.31.255.255");
 ulong cbegin = ip2ulong("192.168.0.0");
 ulong cend = ip2ulong("192.168.255.255");
 isinnerip = isinner(ipnum, abegin, aend) || isinner(ipnum, bbegin, bend) || isinner(ipnum, cbegin, cend) ||
    ip.equals("127.0.0.1");
 return isinnerip;
}
/// <summary>
/// 将ip地址转换为long型数字
/// </summary>
/// <param name="ip">ip地址</param>
/// <returns></returns>
private static ulong ip2ulong(string ip)
{
 byte[] bytes = ipaddress.parse(ip).getaddressbytes();
 ulong ret = 0;
 foreach (var b in bytes)
 {
  ret <<= 8;
  ret |= b;
 }
 return ret;
}
/// <summary>
/// 判断用户ip地址转换为long型后是否在内网ip地址所在范围
/// </summary>
/// <param name="userip">用户ip</param>
/// <param name="begin">开始范围</param>
/// <param name="end">结束范围</param>
/// <returns></returns>
private static bool isinner(ulong userip, ulong begin, ulong end)
{
 return (userip >= begin) && (userip <= end);
}
/// <summary>
/// 获取局域网ip
/// </summary>
/// <returns></returns>
private static string getlanip()
{
 foreach (var hostaddress in dns.gethostaddresses(dns.gethostname()))
 {
  if (hostaddress.addressfamily == addressfamily.internetwork)
  {
   return hostaddress.tostring();
  }
 }
 return string.empty;
}
#endregion

以上这篇基于nginx 反向代理获取真实ip的问题详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网