Python 如何在 Django 中获取用户 IP 地址?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4581789/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I get user IP address in django?
提问by avatar
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.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
采纳答案by yanchenko
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 (e.g. mod_rpafinstalled for Apache).
确保您正确配置了反向代理(如果有)(例如mod_rpaf为 Apache 安装)。
Note: the above uses the firstitem in X-Forwarded-For, but you might want to use the lastitem (e.g., in the case of Heroku: Get client's real IP address on Heroku)
注意:以上使用 中的第一项X-Forwarded-For,但您可能想使用最后一项(例如,在 Heroku 的情况下:在 Heroku 上获取客户端的真实 IP 地址)
And then just pass the request as argument to it;
然后将请求作为参数传递给它;
get_client_ip(request)
回答by Juande Carrion
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. I added the line below in the nginx.conf and fixed the problem: fastcgi_param REMOTE_ADDR $remote_addr;– itgorilla
谢谢你这个好问题。我的 fastcgi 没有传递 REMOTE_ADDR 元键。我在 nginx.conf 中添加了以下行并修复了问题: fastcgi_param REMOTE_ADDR $remote_addr; – 大猩猩
Ps: I added this answer just to make his solution more visible.
Ps:我添加了这个答案只是为了让他的解决方案更明显。
回答by S?var
Alexander's answer is great, but lacks the handling of proxies that sometimes return multiple IP's in the HTTP_X_FORWARDED_FOR header.
亚历山大的回答很好,但缺乏对有时在 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
回答by Doody P
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,因为有些路由器不遵守协议,您可以将内部 ip 视为列表的第一个值。
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.
我希望这可以帮助遇到同样问题的谷歌员工。
回答by un33k
You can use django-ipwarewhich supports Python 2& 3and handles IPv4& IPv6.
您可以使用支持 Python 2和3并处理IPv4和IPv6 的django-ipware。
Install:
安装:
pip install django-ipware
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:
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:
i, r = get_client_ip(request, proxy_count=1)Trusted Proxies - Django server is behind one or more known & trusted proxies:
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.'))
自定义标头 - 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'])代理计数 - Django 服务器支持固定数量的代理:
i, r = get_client_ip(request, proxy_count=1)可信代理 - 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.
注意:阅读本通知。
回答by xxmajia
In my case none of above works, so I have to check uwsgi+ djangosource 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
Django version: (1, 6, 6, 'final', 0)
nginx version: nginx/1.6.0
uwsgi: 2.0.7
环境信息:
python 版本:2.7.5
Django 版本:(1, 6, 6, 'final', 0)
nginx 版本:nginx/1.6.0
uwsgi:2.0.7
Env setting info:
nginx as reverse proxy listening at port 80uwsgi as upstream unix socket, will response to the request eventually
环境设置信息:
nginx 作为反向代理监听端口80uwsgi 作为上游 unix socket,最终会响应请求
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 中指定完全相同的字段/参数名称,并request.META[field/param]在 django 应用程序中使用。
And now you can decide whether to add a middleware (interceptor) or just parse HTTP_X_FORWARDED_FORin certain views.
现在您可以决定是添加中间件(拦截器)还是仅HTTP_X_FORWARDED_FOR在某些视图中进行解析。
回答by ferrix
The reason the functionality was removed from Django originally was that the header cannot ultimately be trusted. The reason is that it is easy to spoof. For example the recommended way to configure an nginx reverse proxy is to:
该功能最初从 Django 中删除的原因是标头最终不能被信任。原因是它很容易被欺骗。例如,配置 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.com will send onwards:
您在 myhost.com 中的 nginx 将继续发送:
X-Forwarded-For: 8.8.8.8, 192.168.1.2, 192.168.1.3
The X-Real-IPwill 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-xff:https: //pypi.python.org/pypi/django-xff/
回答by Lucas03
I was also missing proxy in above answer. I used get_ip_address_from_requestfrom django_easy_timezones.
我在上面的答案中也缺少代理。我get_ip_address_from_request从django_easy_timezones 使用。
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
回答by masterbase
here is a short one liner to accomplish this:
这是一个短的单衬来完成这个:
request.META.get('HTTP_X_FORWARDED_FOR', request.META.get('REMOTE_ADDR', '')).split(',')[0].strip()
回答by CS QGB
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'")

