如何在django中获取用户IP地址?

ava*_*tar 266 python django

如何在django中获取用户的IP?

我有这样的观点:

# Create your views
from django.contrib.gis.utils import GeoIP
from django.template import  RequestContext
from django.shortcuts import render_to_response


def home(request):
  g = GeoIP()
  client_ip = request.META['REMOTE_ADDR']
  lat,long = g.lat_lon(client_ip)
  return render_to_response('home_page_tmp.html',locals())
Run Code Online (Sandbox Code Playgroud)

但我得到这个错误:

KeyError at /mypage/
    'REMOTE_ADDR'
    Request Method: GET
    Request URL:    http://mywebsite.com/mypage/
    Django Version: 1.2.4
    Exception Type: KeyError
    Exception Value:    
    'REMOTE_ADDR'
    Exception Location: /mysite/homepage/views.py in home, line 9
    Python Executable:  /usr/bin/python
    Python Version: 2.6.6
    Python Path:    ['/mysite', '/usr/local/lib/python2.6/dist-packages/flup-1.0.2-py2.6.egg', '/usr/lib/python2.6', '/usr/lib/python2.6/plat-linux2', '/usr/lib/python2.6/lib-tk', '/usr/lib/python2.6/lib-old', '/usr/lib/python2.6/lib-dynload', '/usr/local/lib/python2.6/dist-packages', '/usr/lib/python2.6/dist-packages', '/usr/lib/pymodules/python2.6']
    Server time:    Sun, 2 Jan 2011 20:42:50 -0600
Run Code Online (Sandbox Code Playgroud)

yan*_*nko 393

def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip
Run Code Online (Sandbox Code Playgroud)

确保正确配置了反向代理(如果有)(例如,mod_rpaf为Apache安装).

注意:以上使用了第一X-Forwarded-For,但您可能想要使用最后一项(例如,在Heroku的情况下:在Heroku上获取客户端的真实IP地址)

然后将请求作为参数传递给它;

get_client_ip(request)
Run Code Online (Sandbox Code Playgroud)

  • 这个功能很危险.对于许多设置,恶意用户可以轻松地使此功能返回他们想要的任何地址(而不是真实的地址).请参阅http://esd.io/blog/flask-apps-heroku-real-ip-spoofing.html (45认同)
  • 在视图函数中调用`ip = get_client_ip(request)`. (8认同)
  • 从django docs"依赖REMOTE_ADDR或类似的值被广泛认为是最糟糕的做法"(https://www.djangoproject.com/weblog/2009/jul/28/security/#secondary-issue) (8认同)
  • @jujule这不正确.格式通常是[`X-Forwarded-For:client,proxy1,proxy2`](http://en.wikipedia.org/wiki/X-Forwarded-For).所以第一个地址是客户端. (5认同)
  • 真实的客户端IP地址不是HTTP_X_FORWARDED_FOR中的第一个而是最后一个(参见维基百科页面) (3认同)
  • 不要使用它.如果您的代理(例如nginx)配置不正确,此方法将开始返回所有内容的"127.0.0.1",直到2周后您才会注意到.使用[此答案]建议的`django-ipware`库(http://stackoverflow.com/questions/4581789/how-do-i-get-user-ip-address-in-django/16203978#16203978) . (2认同)
  • 这是堆栈溢出社区智慧失败的一个很好的例子 - 这个答案可能会导致比它潜在解决的问题更多的问题。在某些情况下这可行,但在某些情况下它会崩溃,这意味着这不是一个好的解决方案。当它确实损坏时,您不会知道原因,并且可能无法轻松修复它。正如下面的答案所示,使用 django-ipware 可以避免潜在的心痛。 (2认同)

un3*_*33k 198

您可以使用支持Python 23的django-ipware并处理IPv4IPv6.

安装:

pip install django-ipware

简单用法:

获取客户端的IP地址.

# In a view or a middleware where the `request` object is available

from ipware import get_client_ip
ip, is_routable = get_client_ip(request)
if ip is None:
    # Unable to get the client's IP address
else:
    # We got the client's IP address
    if is_routable:
        # The client's IP address is publicly routable on the Internet
    else:
        # The client's IP address is private

# Order of precedence is (Public, Private, Loopback, None)
Run Code Online (Sandbox Code Playgroud)

高级用法:

自定义标题 - 用于查看ipware的自定义请求标头

i, r = get_client_ip(request, request_header_order=['X_FORWARDED_FOR'])
i, r = get_client_ip(request, request_header_order=['X_FORWARDED_FOR', 'REMOTE_ADDR'])
Run Code Online (Sandbox Code Playgroud)

代理计数 - Django服务器支持固定数量的代理

i, r = get_client_ip(request, proxy_count=1)
Run Code Online (Sandbox Code Playgroud)

受信任的代理 - Django服务器支持一个或多个已知和受信任的代理

i, r = get_client_ip(request, proxy_trusted_ips=('177.2.2.2'))

# For multiple proxies, simply add them to the list
i, r = get_client_ip(request, proxy_trusted_ips=('177.2.2.2', '177.3.3.3'))

# For proxies with fixed sub-domain and dynamic IP addresses, use partial pattern
i, r = get_client_ip(request, proxy_trusted_ips=('177.2.', '177.3.'))
Run Code Online (Sandbox Code Playgroud)

注意:阅读此通知.

  • 看看它的源代码.它处理其他答案所确定的所有并发症. (15认同)
  • 感谢@Heliodor - 是的,我已经使模块对于一般的用例非常简单,并且对于复杂的用例非常灵活.最小的,你想要在推出自己的github页面之前查看它的github页面. (4认同)
  • @ThaJay 请注意,从 2.0.0 开始,您应该使用 `get_client_ip()`。`get_real_ip` 已弃用,并将在 3.0 中删除。 (3认同)
  • 请注意,django-ipware的设置默认不安全!任何人都可以传递其他变量之一,您的网站将记录该IP.始终将`IPWARE_META_PRECEDENCE_LIST`设置为您使用的变量,或使用像https://pypi.python.org/pypi/WsgiUnproxy这样的替代方法 (2认同)

Sæv*_*var 76

亚历山大的答案很棒,但缺乏有时在HTTP_X_FORWARDED_FOR标头中返回多个IP的代理的处理.

真正的IP通常位于列表的末尾,如下所述:http://en.wikipedia.org/wiki/X-Forwarded-For

解决方案是对Alexander代码的简单修改:

def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[-1].strip()
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip
Run Code Online (Sandbox Code Playgroud)

  • 实际上,如果用户位于代理后面,您将获得用户的内部IP地址,即RFC 1918地址.在大多数情况下,这根本不是很理想.此解决方案侧重于获取客户端的外部IP地址(代理地址),这是最右侧的地址. (4认同)
  • 是的,ip位于列表的**开头**.这是错的. (2认同)
  • 谢谢.通常当我从`request.META`请求密钥时,我包含一个默认值,因为标题通常是mising:`request.META.get('REMOTE_ADDR',None)` (2认同)
  • @CarlG你的代码更透明,但get方法继承自django.utils.datastructures.MultiValueDict,默认值为None.但是如果你真的希望它不是None,那么包含默认值肯定是有意义的. (2认同)
  • 除非您正在擦除X-Forwarded-For,当请求命中您的第一台服务器时,该列表中的第一个值是*user supplied*.恶意用户可以轻松欺骗他们想要的任何IP地址.您想要的地址是您的任何服务器之前的第一个IP,不一定是列表中的第一个IP. (2认同)

Par*_*dhu 24

No More confusion In the recent versions of Django it is mentioned clearly that the Ip address of the client is available at

request.META.get("REMOTE_ADDR")
Run Code Online (Sandbox Code Playgroud)

for more info check the Django Docs

  • 当应用程序在反向代理服务器(如 Nginx)后面运行时,这会给出空白值。您将需要“X_FORWARDED_FOR” (4认同)

Doo*_*y P 9

我想建议改进yanchenko的答案.

我没有在X_FORWARDED_FOR列表中取第一个ip,而是采用第一个不是已知内部ip的ip,因为有些路由器不遵守协议,你可以看到内部ips作为列表的第一个值.

PRIVATE_IPS_PREFIX = ('10.', '172.', '192.', )

def get_client_ip(request):
    """get the client ip from the request
    """
    remote_address = request.META.get('REMOTE_ADDR')
    # set the default value of the ip to be the REMOTE_ADDR if available
    # else None
    ip = remote_address
    # try to get the first non-proxy ip (not a private ip) from the
    # HTTP_X_FORWARDED_FOR
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        proxies = x_forwarded_for.split(',')
        # remove the private ips from the beginning
        while (len(proxies) > 0 and
                proxies[0].startswith(PRIVATE_IPS_PREFIX)):
            proxies.pop(0)
        # take the first ip which is not a private one (of a proxy)
        if len(proxies) > 0:
            ip = proxies[0]

    return ip
Run Code Online (Sandbox Code Playgroud)

我希望这可以帮助那些遇到同样问题的Google员工.

  • 从技术上讲,这些前缀 (172, 192) 不一定表示私有地址。 (2认同)
  • 分配给专用网络的地址范围为:172.16.0.0–172.31.255.255(16 个“B 类”网络)、192.168.0.0–192.168.255.255(1 个“B 类”网络)和 10.0.0.0–10.0.0-1255.255.255(16 个“B 类”网络) “A 类”或 256 个“B 类”网络)。 (2认同)

mas*_*ase 7

这是完成此任务的简短说明:

request.META.get('HTTP_X_FORWARDED_FOR', request.META.get('REMOTE_ADDR', '')).split(',')[0].strip()
Run Code Online (Sandbox Code Playgroud)

  • 如果他们都返回 None 那么你会得到一个错误。 (6认同)

Jua*_*ion 6

最简单的解决方案(如果你使用fastcgi + nignx)是itgorilla评论的:

谢谢你提出这个好问题.我的fastcgi没有传递REMOTE_ADDR元键.我在nginx.conf中添加了以下行并修复了问题:fastcgi_param REMOTE_ADDR $ remote_addr; - 伊戈里拉

Ps:我添加了这个答案只是为了让他的解决方案更加明显.

  • nginx(反向代理)和gunicorn 的类似解决方案是什么?当添加到 nginx.conf 时,“proxy_set_header REMOTE_ADDR $remote_addr;”并不能缓解问题。 (3认同)

xxm*_*jia 5

就我而言,上述方法均无效,因此我必须检查uwsgi+ django源代码并在nginx中传递静态参数,并查看原因/方式,以下是我发现的内容。

信封信息:
python版本:2.7.5
Django版本:(1, 6, 6, 'final', 0)
nginx版本:nginx/1.6.0
uwsgi:2.0.7

环境设置信息:
nginx作为反向代理,在端口80 uwsgi处作为上游unix套接字侦听,最终将响应请求

Django配置信息:

USE_X_FORWARDED_HOST = True # with or without this line does not matter
Run Code Online (Sandbox Code Playgroud)

Nginx的配置:

uwsgi_param      X-Real-IP              $remote_addr;
// uwsgi_param   X-Forwarded-For        $proxy_add_x_forwarded_for;
// uwsgi_param   HTTP_X_FORWARDED_FOR   $proxy_add_x_forwarded_for;

// hardcode for testing
uwsgi_param      X-Forwarded-For        "10.10.10.10";
uwsgi_param      HTTP_X_FORWARDED_FOR   "20.20.20.20";
Run Code Online (Sandbox Code Playgroud)

在Django应用中获取所有参数:

X-Forwarded-For :       10.10.10.10
HTTP_X_FORWARDED_FOR :  20.20.20.20
Run Code Online (Sandbox Code Playgroud)

结论:

因此,基本上,您必须在nginx中指定完全相同的字段/参数名称,并request.META[field/param]在django应用中使用。

现在,您可以决定是添加中间件(拦截器)还是仅HTTP_X_FORWARDED_FOR在某些视图中进行解析。