c#获取客户端IP地址(考虑代理)
说明:本文中的内容是我综合博客园上的博文和msdn讨论区的资料,再通过自己的实际测试而得来,属于自己原创的内容说实话很少,写这一篇是为了记录自己在项目中做过的事情,同时也想抛砖引玉。参考的博文及其作者在下文均有提及。待到自己以后对http、tcp/ip等知识学深入了,一定再来这里深入讨论这个内容。
一、名词
首先说一下接下来要讲到的一些名词。
在web开发中,我们大多都习惯使用http请求头中的某些属性来获取客户端的ip地址,常见的属性是remote_addr、http_via和http_x_forwarded_for
。
这三个属性的含义,大概是如此:(摘自网上,欢迎指正)
remote_addr:该属性的值是客户端跟服务器“握手”时候的ip。如果使用了“匿名代理”,remote_addr将显示代理服务器的ip。
x-forwarded-for:是用来识别通过http代理或负载均衡方式连接到web服务器的客户端最原始的ip地址的http请求头字段。
xff的有效性依赖于代理服务器提供的连接原始ip地址的真实性,因此, xff的有效使用应该保证代理服务器是可信的, 比如可以通过建立可信服务器白名单的方式。
这一http头一般格式如下:
x-forwarded-for: client1, proxy1, proxy2
其中的值通过逗号+空格,把多个ip地址区分开, 最左边(client1)是最原始客户端的ip地址, 代理服务器每成功收到一个请求,就把请求来源ip地址添加到右边。 在上面这个例子中,这个请求成功通过了三台代理服务器:proxy1, proxy2 及 proxy3。请求由client1发出,到达了proxy3(proxy3可能是请求的终点)。请求刚从client1中发出时,xff是空的,请求被发往proxy1;通过proxy1的时候,client1被添加到xff中,之后请求被发往proxy2;通过proxy2的时候,proxy1被添加到xff中,之后请求被发往proxy3;通过proxy3时,proxy2被添加到xff中,之后请求的的去向不明,如果proxy3不是请求终点,请求会被继续转发。
鉴于伪造这一字段非常容易,应该谨慎使用x-forwarded-for字段。正常情况下xff中最后一个ip地址是最后一个代理服务器的ip地址, 这通常是一个比较可靠的信息来源。
(另附维基中对x-forwarded-for的完整介绍:http://zh.wikipedia.org/wiki/x-forwarded-for)
至于在使用这些属性的时候,属性的值是什么,网上查到一份这样的博文:获取用户ip地址的三个属性的区别
而在asp.net中,还可以通过另外一种方式获得客户端的ip地址,那就是通过request对象中的userhostaddress属性。在msdn library中,对这个属性是这样解释的:属性值是远程客户端的 ip 地址。
如果客户端使用了代理服务器,那么request.userhostaddress属性获得的就是代理服务器的ip地址。
二、方法
好了,讲了那么多概念性的东西,咱们来讲一下实现的方法。
网上大多数方法的思路是:如果有代理ip,则优先获取代理ip,否则获取连接客户端的ip;或者调转过来,先获取连接客户端的ip,如获取失败,则获取代理ip。
以下方法参考博文asp.net获取客户端ip (作者comeonfyz)
/// <summary> /// 获取客户端ip地址 /// </summary> /// <returns>若失败则返回回送地址</returns> public static string getip() { //如果客户端使用了代理服务器,则利用http_x_forwarded_for找到客户端ip地址 string userhostaddress = httpcontext.current.request.servervariables["http_x_forwarded_for"].tostring().split(',')[0].trim(); //否则直接读取remote_addr获取客户端ip地址 if (string.isnullorempty(userhostaddress)) { userhostaddress = httpcontext.current.request.servervariables["remote_addr"]; } //前两者均失败,则利用request.userhostaddress属性获取ip地址,但此时无法确定该ip是客户端ip还是代理ip if (string.isnullorempty(userhostaddress)) { userhostaddress = httpcontext.current.request.userhostaddress; } //最后判断获取是否成功,并检查ip地址的格式(检查其格式非常重要) if (!string.isnullorempty(userhostaddress) && isip(userhostaddress)) { return userhostaddress; } return "127.0.0.1"; } /// <summary> /// 检查ip地址格式 /// </summary> /// <param name="ip"></param> /// <returns></returns> public static bool isip(string ip) { return system.text.regularexpressions.regex.ismatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$"); }
但是这样做有一个很严重的缺陷,那就是如大牛kingthy在其博文 使用http_x_forwarded_for获取客户端ip的严重后果 中所说的,"http_x_forwarded_for"这个值是通过获取http头的"x_forwarded_for"属性取得的,恶意破坏者可以很轻松地伪造ip地址;而且上文特别提到过,xff的有效性依赖于代理服务器提供的连接原始ip地址的真实性,因此, xff的有效使用应该保证代理服务器是可信的。但是作为开发者,我们既不知道用户的ip地址的真实性,更是难以分辨代理服务器的可信性。
因此,综合各个方面的资料,我个人的想法与大牛kingthy一样:无视代理。
/// <summary> /// 获取客户端ip地址 /// </summary> /// <returns>若失败则返回回送地址</returns> public static string getip() { //如果客户端使用了代理服务器,则利用http_x_forwarded_for找到客户端ip地址 string userhostaddress = httpcontext.current.request.servervariables["http_x_forwarded_for"].tostring().split(',')[0].trim(); //否则直接读取remote_addr获取客户端ip地址 if (string.isnullorempty(userhostaddress)) { userhostaddress = httpcontext.current.request.servervariables["remote_addr"]; } //前两者均失败,则利用request.userhostaddress属性获取ip地址,但此时无法确定该ip是客户端ip还是代理ip if (string.isnullorempty(userhostaddress)) { userhostaddress = httpcontext.current.request.userhostaddress; } //最后判断获取是否成功,并检查ip地址的格式(检查其格式非常重要) if (!string.isnullorempty(userhostaddress) && isip(userhostaddress)) { return userhostaddress; } return "127.0.0.1"; } /// <summary> /// 检查ip地址格式 /// </summary> /// <param name="ip"></param> /// <returns></returns> public static bool isip(string ip) { return system.text.regularexpressions.regex.ismatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$"); }
三、总结
无视代理服务器肯定不是最好的解决方案,如果项目需求明确说要客户端的真实地址,那肯定就不能无视代理服务器了。
另外,我也向artech大牛请教过这方面的问题,他虽然对这些没有深入的研究,但是他也认为没有一种ip获取方式是完全值得信赖的,因为这是tcp/ip协议本身决定的。
附上artech大牛给我的一份资料,分享分享。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: php数值计算num类简单操作示例
下一篇: Glide源码解析一,初始化