From 4da732538f93678381050c78ad7983af4f3adb7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E8=83=9C=E8=88=9F?= <1650473152@qq.com> Date: Mon, 25 Aug 2025 22:01:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=95=E5=85=A5=E6=8E=A5=E5=8F=A3=E9=A2=91?= =?UTF-8?q?=E7=8E=87=E9=99=90=E5=88=B6=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- applications/extensions/__init__.py | 2 ++ applications/extensions/init_error_views.py | 10 ++++++++-- applications/extensions/init_limit.py | 20 ++++++++++++++++++++ applications/view/system/dept.py | 2 ++ applications/view/system/dict.py | 3 +++ applications/view/system/file.py | 2 ++ applications/view/system/mail.py | 2 ++ applications/view/system/power.py | 2 ++ applications/view/system/role.py | 2 ++ applications/view/system/user.py | 2 ++ templates/system/role/main.html | 2 +- 11 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 applications/extensions/init_limit.py diff --git a/applications/extensions/__init__.py b/applications/extensions/__init__.py index 8849131..e27f628 100644 --- a/applications/extensions/__init__.py +++ b/applications/extensions/__init__.py @@ -7,6 +7,7 @@ from .init_mail import init_mail, mail as flask_mail from .init_upload import init_upload from .init_migrate import init_migrate from .init_session import init_session +from .init_limit import init_limit from .init_plugins import register_plugin, broadcast_execute @@ -22,6 +23,7 @@ def init_plugs(app: Flask) -> None: init_upload(app) init_migrate(app) init_session(app) + init_limit(app) # 系统蓝图相关 init_template_directives(app) diff --git a/applications/extensions/init_error_views.py b/applications/extensions/init_error_views.py index 7a88820..e0a812b 100644 --- a/applications/extensions/init_error_views.py +++ b/applications/extensions/init_error_views.py @@ -1,4 +1,4 @@ -from flask import render_template +from flask import render_template, jsonify def init_error_views(app): @@ -12,4 +12,10 @@ def init_error_views(app): @app.errorhandler(500) def internal_server_error(e): - return render_template('errors/500.html'), 500 \ No newline at end of file + return render_template('errors/500.html'), 500 + + @app.errorhandler(429) + def ratelimit_exceeded(e): + return jsonify( + success=False, msg="请求频率超限,请稍后再试。" + ) \ No newline at end of file diff --git a/applications/extensions/init_limit.py b/applications/extensions/init_limit.py new file mode 100644 index 0000000..696ff3e --- /dev/null +++ b/applications/extensions/init_limit.py @@ -0,0 +1,20 @@ +from flask_limiter import Limiter +from flask_login import current_user +from flask import request, Flask + + +# 定义 key 函数:优先使用 current_user.id,否则用 IP +def get_user_identifier(): + # 注意:current_user 可能是 AnonymousUserMixin(未登录) + if hasattr(current_user, 'id') and current_user.is_authenticated: + return str(current_user.id) # 用户 ID 作为 key + return request.remote_addr # 未登录用户则按 IP 限流 + +limiter = Limiter( + key_func=get_user_identifier, + # default_limits=["100 per hour"], # 可选全局默认 + storage_uri="memory://" # 生产建议: "redis://localhost:6379" +) + +def init_limit(app: Flask) -> None: + limiter.init_app(app) \ No newline at end of file diff --git a/applications/view/system/dept.py b/applications/view/system/dept.py index eaf3108..bf1ae70 100644 --- a/applications/view/system/dept.py +++ b/applications/view/system/dept.py @@ -6,6 +6,7 @@ from applications.common.utils.http import success_api, fail_api, table_api from applications.common.utils.rights import authorize from applications.common.utils.validate import str_escape from applications.extensions import db +from applications.extensions.init_limit import limiter from applications.models import Dept, User from applications.schemas import DeptSchema @@ -18,6 +19,7 @@ def main(): @bp.post('/data') +@limiter.limit("60 per minute") @authorize("system:dept:main", log=True) def data(): dept = Dept.query.order_by(Dept.sort).all() diff --git a/applications/view/system/dict.py b/applications/view/system/dict.py index 2c7d75e..5fff51b 100644 --- a/applications/view/system/dict.py +++ b/applications/view/system/dict.py @@ -6,6 +6,7 @@ from applications.common.utils.http import table_api, success_api, fail_api from applications.common.utils.rights import authorize from applications.common.utils.validate import str_escape from applications.extensions import db +from applications.extensions.init_limit import limiter from applications.models import DictType, DictData from applications.schemas import DictTypeOutSchema, DictDataOutSchema @@ -20,6 +21,7 @@ def main(): @bp.get('/dictType/data') +@limiter.limit("60 per minute") @authorize("system:dict:main") def dict_type_data(): # 获取请求参数 @@ -154,6 +156,7 @@ def dict_type_delete(_id): @bp.get('/dictData/data') +@limiter.limit("60 per minute") @authorize("system:dict:main", log=True) def dict_code_data(): type_code = str_escape(request.args.get('typeCode', type=str)) diff --git a/applications/view/system/file.py b/applications/view/system/file.py index 8b6e889..10d8062 100644 --- a/applications/view/system/file.py +++ b/applications/view/system/file.py @@ -4,6 +4,7 @@ from flask import Blueprint, request, render_template, jsonify, current_app from applications.common.utils.http import fail_api, success_api, table_api from applications.common.utils.rights import authorize from applications.extensions import db +from applications.extensions.init_limit import limiter from applications.models import Photo from applications.common.utils import upload as upload_curd @@ -19,6 +20,7 @@ def index(): # 图片数据 @bp.get('/table') +@limiter.limit("60 per minute") @authorize("system:file:main") def table(): page = request.args.get('page', type=int) diff --git a/applications/view/system/mail.py b/applications/view/system/mail.py index 17e4fc1..8a3e91b 100644 --- a/applications/view/system/mail.py +++ b/applications/view/system/mail.py @@ -7,6 +7,7 @@ from applications.common.utils.http import table_api, fail_api, success_api from applications.common.utils.rights import authorize from applications.common.utils.validate import str_escape from applications.extensions import db +from applications.extensions.init_limit import limiter from applications.models import Mail from applications.schemas import MailOutSchema from applications.common.utils import mail @@ -24,6 +25,7 @@ def main(): # 用户分页查询 @bp.get('/data') +@limiter.limit("60 per minute") @authorize("system:mail:main") def data(): # 获取请求参数 diff --git a/applications/view/system/power.py b/applications/view/system/power.py index 9f0e03c..7cdda1b 100644 --- a/applications/view/system/power.py +++ b/applications/view/system/power.py @@ -5,6 +5,7 @@ from applications.common.utils.http import success_api, fail_api, table_api from applications.common.utils.rights import authorize from applications.common.utils.validate import str_escape from applications.extensions import db +from applications.extensions.init_limit import limiter from applications.models import Power from applications.schemas import PowerOutSchema2 from applications.schemas.admin_power import PowerSchema @@ -19,6 +20,7 @@ def index(): @bp.post('/data') +@limiter.limit("60 per minute") @authorize("system:power:main") def data(): power = Power.query.all() diff --git a/applications/view/system/role.py b/applications/view/system/role.py index 888a473..2d01994 100644 --- a/applications/view/system/role.py +++ b/applications/view/system/role.py @@ -6,6 +6,7 @@ from applications.common.utils.http import table_api, success_api, fail_api from applications.common.utils.rights import authorize from applications.common.utils.validate import str_escape from applications.extensions import db +from applications.extensions.init_limit import limiter from applications.models import Role, Power, User from applications.schemas import RoleOutSchema, PowerOutSchema2 @@ -20,6 +21,7 @@ def main(): # 表格数据 @bp.get('/data') +@limiter.limit("60 per minute") @authorize("system:role:main") def table(): role_name = str_escape(request.args.get('roleName', type=str)) diff --git a/applications/view/system/user.py b/applications/view/system/user.py index ee96771..44dcb18 100644 --- a/applications/view/system/user.py +++ b/applications/view/system/user.py @@ -8,6 +8,7 @@ from applications.common.utils.http import table_api, fail_api, success_api from applications.common.utils.rights import authorize from applications.common.utils.validate import str_escape from applications.extensions import db +from applications.extensions.init_limit import limiter from applications.models import Role, Dept from applications.models import User, AdminLog @@ -23,6 +24,7 @@ def main(): # 用户分页查询 @bp.get('/data') +@limiter.limit("60 per minute") @authorize("system:user:main") def data(): # 获取请求参数 diff --git a/templates/system/role/main.html b/templates/system/role/main.html index 09ed2eb..aae0d4f 100644 --- a/templates/system/role/main.html +++ b/templates/system/role/main.html @@ -1,7 +1,7 @@ - 用户管理 + 角色管理 {% include 'system/common/header.html' %}