【Flask网站获取IIS通过URL重写进入的访问者IP】
前言:
我在flask网站项目的登录页面中,有一个逻辑是获取访问者的IP地址,并且记录,如果访问者密码输入错误超过1小时20次,我就禁止此IP登录,可是,我把网站放在IIS上,IIS通过URL重写来进入网站,以至于我的网站后台python代码获取的IP地址是本机的IIS地址,也就是127.0.0.1,这种情况我们这样解决:
原因:
在 IIS 环境中使用 URL 重写时,Flask 获取到 127.0.0.1 是因为请求经过 IIS 转发后,原始客户端 IP 被隐藏了。要解决这个问题,需要配置 IIS 传递原始 IP 地址,并在 Flask 中正确获取。
解决办法:
配置 IIS 传递原始 IP
在 IIS 的 URL 重写规则中,确保添加了传递原始 IP 的设置,修改你的 web.config 文件
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="ReverseProxyInboundRule1" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="http://localhost:5000/{R:1}" />
<!-- 添加这行传递原始IP -->
<serverVariables>
<set name="HTTP_X_FORWARDED_FOR" value="{REMOTE_ADDR}" />
</serverVariables>
</rule>
</rules>
</rewrite>
<!-- 允许修改HTTP_X_FORWARDED_FOR服务器变量 -->
<security>
<requestFiltering>
<serverVariables>
<add name="HTTP_X_FORWARDED_FOR" allowDefinition="Everywhere" />
</serverVariables>
</requestFiltering>
</security>
</system.webServer>
</configuration>
然后在 Flask 中获取真实 IP:
from flask import Flask, request, render_template
import time
app = Flask(__name__)
# 存储IP地址和失败尝试的字典
failed_attempts = {}
def get_real_ip():
"""获取客户端真实IP地址"""
# 从代理头获取IP,如果存在的话
x_forwarded_for = request.headers.get('X-Forwarded-For')
if x_forwarded_for:
# X-Forwarded-For可能包含多个IP,取第一个
return x_forwarded_for.split(',')[0].strip()
# 否则使用默认的remote_addr
return request.remote_addr
def is_ip_blocked(ip):
"""检查IP是否被封锁"""
if ip not in failed_attempts:
return False
attempts = failed_attempts[ip]
# 检查1小时内失败次数是否超过20次
now = time.time()
# 只保留1小时内的记录
attempts = [t for t in attempts if now - t < 3600]
failed_attempts[ip] = attempts
return len(attempts) >= 20
@app.route('/login', methods=['GET', 'POST'])
def login():
ip = get_real_ip()
# 检查IP是否被封锁
if is_ip_blocked(ip):
return render_template('login.html', error='您的IP因多次登录失败已被暂时封锁')
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
# 验证用户名和密码
if not verify_credentials(username, password):
# 记录失败尝试
if ip not in failed_attempts:
failed_attempts[ip] = []
failed_attempts[ip].append(time.time())
return render_template('login.html', error='用户名或密码错误')
# 登录成功
return redirect('/dashboard')
return render_template('login.html')
def verify_credentials(username, password):
"""验证用户名和密码的函数(示例)"""
# 实际应用中这里应该连接数据库验证
return username == 'admin' and password == 'secret'
if __name__ == '__main__':
app.run()
配置 Flask 信任代理,如果你的 Flask 应用位于多个代理之后,需要配置信任代理:
from werkzeug.middleware.proxy_fix import ProxyFix
# 根据实际代理层数调整参数
app.wsgi_app = ProxyFix(
app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1, x_prefix=1
)
这些配置的原理是让 IIS 在转发请求时将原始客户端 IP 地址放在X-Forwarded-For头中,然后 Flask 应用从这个头中获取真实的客户端 IP 地址,而不是 IIS 服务器的地址。
完成这些配置后,你的登录限制逻辑就能基于真实的客户端 IP 地址工作了。