from flask import Flask, request
from werkzeug.middleware.proxy_fix import ProxyFix
app = Flask(__name__)
# 修复反向代理下的客户端IP
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1, x_prefix=1)
def get_client_public_ip():
"""获取客户端真实公网IP"""
if request.remote_addr:
return request.remote_addr
x_forwarded_for = request.headers.get('X-Forwarded-For')
if x_forwarded_for:
return x_forwarded_for.split(',')[0].strip()
x_real_ip = request.headers.get('X-Real-IP')
if x_real_ip:
return x_real_ip
return request.remote_addr
@app.route('/')
def index():
client_ip = get_client_public_ip()
headers_info = {
'Remote Address': request.remote_addr,
'X-Forwarded-For': request.headers.get('X-Forwarded-For', '—'),
'X-Real-IP': request.headers.get('X-Real-IP', '—'),
'User-Agent': request.headers.get('User-Agent', '—'),
'Accept-Language': request.headers.get('Accept-Language', '—'),
'Protocol': request.scheme.upper(),
}
# 构建 HTML 页面(含复制功能)
html = f"""
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>公网IP查询</title>
<style>
* {{
margin: 0;
padding: 0;
box-sizing: border-box;
}}
body {{
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #333;
line-height: 1.6;
min-height: 100vh;
padding: 20px;
}}
.container {{
max-width: 800px;
margin: 40px auto;
background: white;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
overflow: hidden;
}}
.header {{
background: #6a20dd;
color: white;
padding: 30px;
text-align: center;
}}
.header h1 {{
font-size: 2.2em;
margin-bottom: 10px;
}}
.header p {{
font-size: 1.1em;
opacity: 0.9;
}}
.content {{
padding: 30px;
}}
.ip-box {{
display: flex;
align-items: center;
justify-content: space-between;
margin: 20px 0;
padding: 20px;
background: #f7fafc;
border: 2px dashed #667eea;
border-radius: 12px;
font-size: 1.8em;
color: #4a5568;
font-weight: bold;
gap: 15px;
}}
.ip-value {{
flex-grow: 1;
word-break: break-all;
}}
.copy-btn {{
background: #667eea;
color: white;
border: none;
padding: 10px 18px;
border-radius: 8px;
cursor: pointer;
font-size: 1em;
transition: background 0.3s;
}}
.copy-btn:hover {{
background: #5a67d8;
}}
.copy-success {{
color: #38a169;
font-size: 0.9em;
margin-top: 5px;
opacity: 0;
height: 1.2em;
transition: opacity 0.3s;
}}
.show {{
opacity: 1 !important;
}}
.info-table {{
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}}
.info-table th {{
text-align: left;
padding: 12px 15px;
background: #f1f5f9;
color: #2d3748;
font-weight: 600;
width: 30%;
}}
.info-table td {{
padding: 12px 15px;
border-bottom: 1px solid #e2e8f0;
color: #4a5568;
}}
.info-table tr:hover {{
background-color: #f8f9fc;
}}
footer {{
text-align: center;
padding: 20px;
color: #a0aec0;
font-size: 0.9em;
background: #f7fafc;
border-top: 1px solid #e2e8f0;
}}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🌐 公网 IP 查询</h1>
<p>查看您的真实公网 IP 地址与请求信息</p>
</div>
<div class="content">
<div class="ip-box">
<span class="ip-value" id="ip-value">{client_ip}</span>
<button class="copy-btn" onclick="copyIP()">📋 复制</button>
</div>
<div class="copy-success" id="copy-success">✅ 复制成功!</div>
<h2>📝 请求详情</h2>
<table class="info-table">
{''.join(f"<tr><th>{key}</th><td>{value}</td></tr>" for key, value in headers_info.items())}
</table>
</div>
<footer>
Powered by Flask | 保护您的隐私,仅用于调试与部署
</footer>
</div>
<script>
function copyIP() {{
const ipValue = document.getElementById('ip-value').innerText;
const successMsg = document.getElementById('copy-success');
const btn = document.querySelector('.copy-btn');
// 优先使用现代 Clipboard API(仅在安全上下文可用)
if (navigator.clipboard && window.isSecureContext) {{
navigator.clipboard.writeText(ipValue).then(() => {{
showSuccess();
}}).catch(err => {{
console.warn('Clipboard API 失败,尝试降级方案:', err);
fallbackCopy(ipValue);
}});
}} else {{
// 降级:使用 document.execCommand
fallbackCopy(ipValue);
}}
function fallbackCopy(text) {{
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.left = '-999999px';
textarea.style.top = '-999999px';
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
try {{
document.execCommand('copy');
showSuccess();
}} catch (err) {{
console.error('Fallback 复制失败:', err);
alert('复制失败,请手动选择复制。');
}} finally {{
document.body.removeChild(textarea);
}}
}}
function showSuccess() {{
successMsg.classList.add('show');
btn.innerText = "✅ 已复制";
setTimeout(() => {{
successMsg.classList.remove('show');
btn.innerText = "📋 复制";
}}, 2000);
}}
}}
</script>
</body>
</html>
"""
return html
if __name__ == '__main__':
print("✅ Flask 服务已启动!")
print("👉 访问地址: http://127.0.0.1:81")
print("💡 提示: 在非 HTTPS 或非 localhost 环境下,复制功能将自动降级以确保兼容性。")
app.run(host='0.0.0.0', port=81, debug=True)
python(56) : WEB接口获取来源公网IP
于 2025-10-28 10:20:53 首次发布

被折叠的 条评论
为什么被折叠?



