简体   繁体   English

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

[英]How do I get user IP address in Django?

How do I get user's IP in Django?如何在 Django 中获取用户的 IP?

I have a view like this:我有这样的看法:

# 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())

But I get this error:但我收到此错误:

KeyError at /mypage/
    'REMOTE_ADDR'
    Request Method: GET
    Request URL:    http://mywebsite.example/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
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

Make sure you have reverse proxy (if any) configured correctly (eg mod_rpaf installed for Apache).确保您正确配置了反向代理(如果有)(例如为 Apache 安装了mod_rpaf )。

Note: the above uses the first item in X-Forwarded-For , but you might want to use the last item (eg, in the case of Heroku: Get client's real IP address on Heroku )注意:上面使用X-Forwarded-For中的第一项,但您可能希望使用最后一项(例如,在 Heroku 的情况下: Get client's real IP address on Heroku

And then just pass the request as argument to it;然后将请求作为参数传递给它;

get_client_ip(request)

Django documentation for HttpRequest.META HttpRequest.META 的 Django 文档

You can use django-ipware which supports Python 2 & 3 and handles IPv4 & IPv6 .您可以使用支持 Python 23并处理IPv4IPv6的 django-ipware

Install:安装:

pip install django-ipware

Simple Usage:简单用法:

# 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)

Advanced Usage:高级用法:

  • Custom Header - Custom request header for ipware to look at:自定义标头 - 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'])
  • Proxy Count - Django server is behind a fixed number of proxies:代理计数 - Django 服务器位于固定数量的代理后面:

     i, r = get_client_ip(request, proxy_count=1)
  • Trusted Proxies - Django server is behind one or more known & trusted proxies:受信任的代理 - 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.'))

Note: read this notice .注意:阅读本通知

Alexander's answer is great, but lacks the handling of proxies that sometimes return multiple IP's in the HTTP_X_FORWARDED_FOR header. Alexander 的回答很好,但缺少对有时在 HTTP_X_FORWARDED_FOR 标头中返回多个 IP 的代理的处理。

The real IP is usually at the end of the list, as explained here: http://en.wikipedia.org/wiki/X-Forwarded-For真实 IP 通常位于列表末尾,如下所述: http ://en.wikipedia.org/wiki/X-Forwarded-For

The solution is a simple modification of Alexander's code:解决方案是简单修改 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

No More confusion In the recent versions of Django it is mentioned clearly that the Ip address of the client is available at不再混淆在最近的 Django 版本中,明确提到客户端的 IP 地址可在

request.META.get("REMOTE_ADDR")

for more info check the Django Docs有关更多信息,请查看Django 文档

I would like to suggest an improvement to yanchenko's answer.我想建议对 yanchenko 的回答进行改进。

Instead of taking the first ip in the X_FORWARDED_FOR list, I take the first one which in not a known internal ip, as some routers don't respect the protocol, and you can see internal ips as the first value of the list.我没有取 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

I hope this helps fellow Googlers who have the same problem.我希望这对有同样问题的谷歌同事有所帮助。

这是一个简短的班轮来实现这一点:

request.META.get('HTTP_X_FORWARDED_FOR', request.META.get('REMOTE_ADDR', '')).split(',')[0].strip()

In my case none of above works, so I have to check uwsgi + django source code and pass static param in nginx and see why/how, and below is what I have found.在我的情况下,以上都不起作用,所以我必须检查uwsgi + django源代码并在 nginx 中传递静态参数,看看为什么/如何,下面是我发现的。

Env info:环境信息:
python version: 2.7.5蟒蛇版本: 2.7.5
Django version: (1, 6, 6, 'final', 0) Django 版本: (1, 6, 6, 'final', 0)
nginx version: nginx/1.6.0 nginx版本: nginx/1.6.0
uwsgi: 2.0.7 uwsgi: 2.0.7

Env setting info:环境设置信息:
nginx as reverse proxy listening at port 80 uwsgi as upstream unix socket, will response to the request eventually nginx 作为反向代理监听80端口 uwsgi 作为上游 unix 套接字,最终将响应请求

Django config info: Django 配置信息:

USE_X_FORWARDED_HOST = True # with or without this line does not matter

nginx config: 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";

getting all the params in django app:获取 django 应用程序中的所有参数:

X-Forwarded-For :       10.10.10.10
HTTP_X_FORWARDED_FOR :  20.20.20.20

Conclusion:结论:

So basically, you have to specify exactly the same field/param name in nginx, and use request.META[field/param] in django app.所以基本上,你必须在 nginx 中指定完全相同的字段/参数名称,并在 django 应用程序中使用request.META[field/param]

And now you can decide whether to add a middleware (interceptor) or just parse HTTP_X_FORWARDED_FOR in certain views.现在您可以决定是添加中间件(拦截器)还是仅在某些视图中解析HTTP_X_FORWARDED_FOR

The simpliest solution (in case you are using fastcgi+nignx) is what itgorilla commented:最简单的解决方案(如果您使用的是 fastcgi+nignx)是 itgorilla 评论的:

Thank you for this great question.谢谢你提出这个好问题。 My fastcgi was not passing the REMOTE_ADDR meta key.我的 fastcgi 没有通过 REMOTE_ADDR 元键。 I added the line below in the nginx.conf and fixed the problem: fastcgi_param REMOTE_ADDR $remote_addr;我在 nginx.conf 中添加了以下行并修复了问题:fastcgi_param REMOTE_ADDR $remote_addr; – itgorilla – 伊大猩猩

Ps: I added this answer just to make his solution more visible. Ps:我添加这个答案只是为了让他的解决方案更加明显。

The reason the functionality was removed from Django originally was that the header cannot ultimately be trusted.最初从 Django 中删除该功能的原因是标头最终不能被信任。 The reason is that it is easy to spoof.原因是它很容易被欺骗。 For example the recommended way to configure an Nginx reverse proxy is to:例如,配置 Nginx 反向代理的推荐方法是:

add_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header X-Real-Ip       $remote_addr;

When you do:当你这样做时:

curl -H 'X-Forwarded-For: 8.8.8.8, 192.168.1.2' http://192.168.1.3/

Your Nginx in myhost.example will send onwards:您在myhost.example中的 Nginx 将继续发送:

X-Forwarded-For: 8.8.8.8, 192.168.1.2, 192.168.1.3

The X-Real-IP will be the IP of the first previous proxy if you follow the instructions blindly.如果您盲目地按照说明操作, X-Real-IP将是第一个代理的 IP。

In case trusting who your users are is an issue, you could try something like django-xff : https://pypi.python.org/pypi/django-xff/如果信任您的用户是一个问题,您可以尝试类似django-xffhttps ://pypi.python.org/pypi/django-xff/

I was also missing proxy in above answer.我在上面的答案中也缺少代理。 I used get_ip_address_from_request from django_easy_timezones .我使用了get_ip_address_from_requestget_ip_address_from_request

from easy_timezones.utils import get_ip_address_from_request, is_valid_ip, is_local_ip
ip = get_ip_address_from_request(request)
try:
    if is_valid_ip(ip):
        geoip_record = IpRange.objects.by_ip(ip)
except IpRange.DoesNotExist:
    return None

And here is method get_ip_address_from_request , IPv4 and IPv6 ready:这里是get_ip_address_from_request方法,准备好 IPv4 和 IPv6:

def get_ip_address_from_request(request):
    """ Makes the best attempt to get the client's real IP or return the loopback """
    PRIVATE_IPS_PREFIX = ('10.', '172.', '192.', '127.')
    ip_address = ''
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '')
    if x_forwarded_for and ',' not in x_forwarded_for:
        if not x_forwarded_for.startswith(PRIVATE_IPS_PREFIX) and is_valid_ip(x_forwarded_for):
            ip_address = x_forwarded_for.strip()
    else:
        ips = [ip.strip() for ip in x_forwarded_for.split(',')]
        for ip in ips:
            if ip.startswith(PRIVATE_IPS_PREFIX):
                continue
            elif not is_valid_ip(ip):
                continue
            else:
                ip_address = ip
                break
    if not ip_address:
        x_real_ip = request.META.get('HTTP_X_REAL_IP', '')
        if x_real_ip:
            if not x_real_ip.startswith(PRIVATE_IPS_PREFIX) and is_valid_ip(x_real_ip):
                ip_address = x_real_ip.strip()
    if not ip_address:
        remote_addr = request.META.get('REMOTE_ADDR', '')
        if remote_addr:
            if not remote_addr.startswith(PRIVATE_IPS_PREFIX) and is_valid_ip(remote_addr):
                ip_address = remote_addr.strip()
    if not ip_address:
        ip_address = '127.0.0.1'
    return ip_address

In django.VERSION (2, 1, 1, 'final', 0) request handler在 django.VERSION (2, 1, 1, 'final', 0) 请求处理程序中

sock=request._stream.stream.raw._sock
#<socket.socket fd=1236, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.1.111', 8000), raddr=('192.168.1.111', 64725)>
client_ip,port=sock.getpeername()

if you call above code twice,you may got如果你两次调用上面的代码,你可能会得到

AttributeError("'_io.BytesIO' object has no attribute 'stream'",) AttributeError("'_io.BytesIO' 对象没有属性 'stream'",)

AttributeError("'LimitedStream' object has no attribute 'raw'") AttributeError("'LimitedStream' 对象没有属性 'raw'")

Simply add只需添加

{{ request.META.REMOTE_ADDR }}

In Django-Template where you want the user to see their IP address.在 Django-Template 中,您希望用户看到他们的 IP 地址。 That is if you are not interested in saving this to the DB.也就是说,如果您对将其保存到数据库不感兴趣。

After getting ip address you need to find location获得 ip 地址后,您需要找到位置

# pip install geocoder

import geocoder

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]
        ip_location = geocoder.ip(f"{ip}")
        ip_location = geocoder.ip("me")
        print(ip_location.city)
        # you can get city such as "New York"
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

Get the ip address with this function:使用这个 function 获取 ip 地址:

def get_ip_address(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

after that you can get the user location data and other info from that web app http://www.iplocinfo.com/ :之后,您可以从 web 应用程序http://www.iplocinfo.com/获取用户位置数据和其他信息:

import requests
def get_ip_data(request):
    ip_address = get_ip_address(request)
    api_key = "your api key"
    endPoint = f'https://www.iplocinfo.com/api/v1/{ip_address}?apiKey={api_key}'
    data = requests.get(endPoint)
    return data.json()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM