使用Python的Flask获取访客的IP地址

328
我正在制作一个网站,用户可以使用Flask微框架(基于Werkzeug)登录并下载文件,该框架使用Python(在我的情况下是2.6)。
当用户登录时,我需要获取其IP地址(用于记录目的)。 有人知道如何做到这一点吗?肯定有一种方法可以使用Python实现。
12个回答

432

请参阅如何访问请求对象的文档,然后从同一请求对象中获取属性remote_addr

代码示例

from flask import request
from flask import jsonify

@app.route("/get_my_ip", methods=["GET"])
def get_my_ip():
    return jsonify({'ip': request.remote_addr}), 200

更多信息请参见Werkzeug文档


2
有时候,它可能会很有用:request.access_route[0] - Jonathan Simon Prates
80
关于nginx,它会在HTTP_X_FORWARDED_FOR下发送真实IP地址,因此确保您不会在每个请求中得到本地主机。 - Rasty Turek

133

代理可能会让这变得有点棘手,请确保查看ProxyFixFlask文档)如果您正在使用代理,则请查看特定环境中的request.environ。对于nginx,我有时会做这样的事情:

from flask import request   
request.environ.get('HTTP_X_REAL_IP', request.remote_addr)   

当代理服务器(如nginx)转发地址时,它们通常会在请求头中的某个地方包含原始IP。

更新 请查看flask-security的实现。请务必在实施之前查阅ProxyFix文档。根据您特定的环境,您的解决方案可能会有所不同。


2
当您在反向代理的配置中设置适当的字段时,此方法可正常工作。已在生产环境中使用。 - drahnr
4
@drahnr 是的,确实如此。如果在例如nginx中设置了以下内容,则上述代码将起作用:proxy_set_header X-Real-IP $remote_addr; - pors
@pors - 它有效。我的nginx配置与你的相同,但我仍然不明白为什么代码:request.headers.get('X-Real-IP', request.remote_addr) 不起作用。请注意,直觉上我会从头部获取值并使用名称“X-Real-IP”,因为这是我的nginx配置方式。 - lostdorje

97

实际上,你会发现只需获取以下内容即可获得服务器地址:

request.remote_addr

如果你想获取客户端的IP地址,可以使用以下代码:

request.environ['REMOTE_ADDR']

4
只有在使用反向代理的情况下才需要这样做,对吗? - Ry-
4
我认为你的代码不应该关心你是否在代理后面。如果此选项可靠地工作,无论是否使用反向代理,而另一个选项则假定你没有在反向代理后面,则应优先选择前者。(如果我能轻松设置反向代理,我会测试这一点,但目前花费的时间比我有的时间更多。) - jpmc26
3
使用mod_wsgi时,request.remote_addr每次返回服务器地址。使用request.environ['REMOTE_ADDR']可以获取客户端的公共IP地址。也许我漏掉了什么? - Chiedo
2
@jpmc26,如果你在使用远程代理时,你通常需要关注它。因为此时连接到你的IP将是远程代理服务器的IP,你需要依赖于它添加的头信息来获取原始客户端IP。如果你没有使用远程代理,这些头信息通常不会存在,你需要使用连接IP作为客户端IP。但你不能只检查头信息并在其不存在时回退到连接IP,因为如果你没有使用反向代理,客户端可以伪造他们的IP,这在许多情况下都会成为安全问题。 - Mark Amery
@jpmc26 原则上,框架可以为您处理此问题,但前提是您在其配置中明确告诉它是否在反向代理后面。另外,据我所知,没有框架实际处理这个问题,因此现实情况是应用程序代码确实需要关注,即使我们可以想象一个假设的世界,在那里这些东西由框架处理。反向代理不可能负责处理这个问题,因为它无法向其后面的服务器通信IP,而该IP不能被客户端欺骗,客户端与不在代理后面的服务器通信。 - Mark Amery
显示剩余2条评论

55

以下代码总是给出客户端的公共IP地址(而不是代理后面的私有IP地址)。

from flask import request

if request.environ.get('HTTP_X_FORWARDED_FOR') is None:
    print(request.environ['REMOTE_ADDR'])
else:
    print(request.environ['HTTP_X_FORWARDED_FOR']) # if behind a proxy

4
这个答案连同根据这篇博客更改我的nginx配置对我有用。https://calvin.me/forward-ip-addresses-when-using-nginx-proxy/ - Romano Vacca

37

我有 Nginx,以下是我的Nginx配置:

server {
    listen 80;
    server_name xxxxxx;
    location / {
               proxy_set_header   Host                 $host;
               proxy_set_header   X-Real-IP            $remote_addr;
               proxy_set_header   X-Forwarded-For      $proxy_add_x_forwarded_for;
               proxy_set_header   X-Forwarded-Proto    $scheme;

               proxy_pass http://x.x.x.x:8000;
        }
}

@tirtha-r 的解决方案对我有用。

#!flask/bin/python
from flask import Flask, jsonify, request
app = Flask(__name__)

@app.route('/', methods=['GET'])
def get_tasks():
    if request.environ.get('HTTP_X_FORWARDED_FOR') is None:
        return jsonify({'ip': request.environ['REMOTE_ADDR']}), 200
    else:
        return jsonify({'ip': request.environ['HTTP_X_FORWARDED_FOR']}), 200

if __name__ == '__main__':
    app.run(debug=True,host='0.0.0.0', port=8000)

我的请求和响应:

curl -X GET http://test.api

{
    "ip": "Client Ip......"
}

1
我混合了@pegasus和你的答案,效果很好! - egvo

35

可以使用以下代码片段检索用户的IP地址:

from flask import request
print(request.remote_addr)

9
如果应用程序在代理服务器(如_nginx_)后运行,这就不正确了。这在生产环境中经常发生。 - datashaman

10

httpbin.org 使用了这种方法:

return jsonify(origin=request.headers.get('X-Forwarded-For', request.remote_addr))

1
由于代理的原因,返回了 127.0.0.1,并不是很有用。 - Uri Goren
谢谢,这是唯一对我有用的答案。 - English Rain

10

如果您在其他负载均衡器(例如AWS应用程序负载均衡器)后面使用Nginx,则HTTP_X_FORWARDED_FOR返回地址列表。可以通过以下方式进行修复:

if 'X-Forwarded-For' in request.headers:
    proxy_data = request.headers['X-Forwarded-For']
    ip_list = proxy_data.split(',')
    user_ip = ip_list[0]  # first address in list is User IP
else:
    user_ip = request.remote_addr  # For local development

2
注意:如果没有在代理后端提供服务,则此方法不安全。请尝试使用ProxyFix代替。 - Arel

8

以下是最简单的解决方案,以及如何在生产中使用。

from flask import Flask, request
from werkzeug.middleware.proxy_fix import ProxyFix
app = Flask(__name__)
# Set environment from any X-Forwarded-For headers if proxy is configured properly
app.wsgi_app = ProxyFix(app.wsgi_app, x_host=1)

@app.before_request
def before_process():
   ip_address = request.remote_addr

/etc/nginx/sites-available/$project中添加include proxy_params
  location / {
    proxy_pass http://unix:$project_dir/gunicorn.sock;
    include proxy_params;
  }

include proxy_params包含了一些被ProxyFix解析的头信息并进行转发。

$ sudo cat /etc/nginx/proxy_params 
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

如果您正在使用Argo隧道,可以确认此代码仅使用上述代码即可正常工作,无需进行任何修改。似乎“ProxyFix”已经发挥了其魔力。 - Muneeb Ahmad Khurram

1
如果您正在使用Gunicorn和Nginx环境,则以下代码模板适用于您。
addr_ip4 = request.remote_addr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接