目录
一、需求描述
基础封禁数据概览
以卡片形式呈现多维度封禁统计值,涵盖今日、本周、本月的封禁数量,累计封禁总数,涉及攻击来源国家数,以及资产统计数,快速展示防护成果与规模 。
近期封禁趋势分析
通过柱状图呈现最近一周(08 - 05 至 08 - 11)每日封禁 IP 动态,不同颜色柱体叠加展示当日不同攻击来源(对应图例国家 / 地区)的封禁情况,辅助分析攻击波峰波谷与来源变化 。
地理分布可视化
借助环形饼图,呈现最近一周被封禁 IP 的地理来源占比,不同颜色区块对应不同国家 / 地区,直观展现攻击源的地域集中性 。
攻击源排名洞察
以柱状图展示 “TOP 30 国家攻击 IP 统计”,按攻击贡献排序,清晰呈现攻击最频繁的前 30 个国家 / 地区及对应封禁量,为重点防护方向提供数据支撑 。
二、设计实现
2.1 geoip2 模块
import geoip2.database
from IPy import IP
import re
from datetime import datetime
from applications.extensions import db
from applications.models import Hwip
def get_ip_location(ip_address, db_path='./GeoLite2-City.mmdb'):
"""查询 IP 地址的地理位置信息(优化版)"""
try:
cidr_pattern = r'^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/([0-2]?[0-9]|3[0-2])$'
if re.match(cidr_pattern, ip_address):
ips = IP(ip_address)
for ip in ips:
with geoip2.database.Reader(db_path) as reader:
# 优先使用中文查询,失败时回退到英文
try:
response = reader.city(str(ip), locale='zh-CN')
except Exception:
response = reader.city(str(ip))
# 提取地理位置信息
country = response.country.names.get('zh-CN') or response.country.name
city = response.city.names.get('zh-CN') or response.city.name
province = response.subdivisions.most_specific.names.get('zh-CN') or response.subdivisions.most_specific.name
timestamp = datetime.now()
print(country,city,province)
if country == '中华民国' or country == '香港':
country == '中国'
if country == '大韩民国':
country == '韩国'
rule = Hwip(timestamp=timestamp, ip=str(ip), country=country, city=city, province=province)
db.session.add(rule)
db.session.commit()
except geoip2.errors.AddressNotFoundError:
print(f"错误:未找到 IP {ip} 的地理位置信息")
return None
except Exception as e:
print(f"查询失败:{str(e)}")
return None
2.2 数据库
from applications.extensions import db
class Hwip(db.Model):
__tablename__ = 'admin_hwip'
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
timestamp = db.Column(db.String(20), nullable=False, comment='创建时间')
ip = db.Column(db.String(20), nullable=False, comment='封禁|解封的IP')
country = db.Column(db.String(20), nullable=False)
city = db.Column(db.String(80), comment='城市')
province = db.Column(db.String(80), comment='省份')
2.3 前端页面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>分析页</title>
{% include 'system/common/header.html' %}
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="{
{ url_for('static', filename='system/admin/css/other/analysis.css') }}"/>
</head>
<body>
<div class="pear-container">
<div class="layui-row layui-col-space12">
<div class="layui-col-xs6 layui-col-md2">
<div class="layui-card top-panel">
<div class="layui-card-header">今日封禁</div>
<div class="layui-card-body">
<div class="layui-row layui-col-space5">
<div class="layui-col-xs8 layui-col-md8 top-panel-number" style="color: #28333E;"
id="value1">
0
</div>
<div class="layui-col-xs4 layui-col-md4 top-panel-tips">
<svg t="1688201011061" class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="1411" width="200" height="200">
<path
d="M716.8 0H307.2C137.5488 0 0 137.5488 0 307.2v409.6c0 169.6512 137.5488 307.2 307.2 307.2h409.6c169.6512 0 307.2-137.5488 307.2-307.2V307.2c0-169.6512-137.5488-307.2-307.2-307.2z"
fill="#D8F4EE" p-id="1412"></path>
<path
d="M691.8656 639.1296a257.4592 257.4592 0 0 1-52.736 52.736l60.928 60.928a37.2992 37.2992 0 0 0 52.736-52.736l-60.928-60.928z"
fill="#75E8CD" p-id="1413"></path>
<path
d="M486.4 716.8a230.4 230.4 0 1 0 0-460.8 230.4 230.4 0 0 0 0 460.8z m130.9696-302.5152a16.64 16.64 0 0 1 0.3584 23.5008l-115.84 119.296a16.5376 16.5376 0 0 1-3.84 3.3792 16.64 16.64 0 0 1-22.144-3.5072l-67.328-60.5184-47.232 48.64a16.64 16.64 0 0 1-23.8336-23.168l56.32-57.984a16.6912 16.6912 0 0 1 24.8832-3.072l69.5552 62.5152 105.6-108.7232a16.6144 16.6144 0 0 1 23.5008-0.3584z"
fill="#01C3A3" p-id="1414"></path>
<path
d="M341.257236 436.535528m-7.948259-21.87091l0 0q-7.948258-21.87091-29.819168-13.922652l0 0q-21.87091 7.948258-13.922652 29.819169l0 0q7.948258 21.87091 29.819168 13.922651l0 0q21.87091-7.948258 13.922652-29.819168Z"
fill="#FFFFFF" p-id="1415"></path>
<path
d="M331.4432 370.7392a17.2544 17.2544 0 0 0 29.3888-3.584l5.9648-13.5168a26.112 26.112 0 0 1 14.9504-13.9264l30.1056-10.9312a15.2576 15.2576 0 0 0 8.8832-20.224 15.0272 15.0272 0 0 0-18.4576-8.6016c-47.7696 15.4368-67.0976 29.3888-73.9584 57.5232a15.6672 15.6672 0 0 0 3.1488 13.2608z"
fill="#FFFFFF" p-id="1416"></path>
</svg>
</div>
</div>
</div>
</div>
</div>
<div class="layui-col-xs6 layui-col-md2">
<div class="layui-card top-panel">
<div class="layui-card-header">本周封禁</div>
<div class="layui-card-body">
<div class="layui-row layui-col-space5">
<div class="layui-col-xs8 layui-col-md8 top-panel-number" style="color: #28333E;"
id="value2">
0
</div>
<div class="layui-col-xs4 layui-col-md4 top-panel-tips">
<svg t="1688201051691

最低0.47元/天 解锁文章

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



