引入接口频率限制机制
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from flask import render_template
|
||||
from flask import render_template, jsonify
|
||||
|
||||
|
||||
def init_error_views(app):
|
||||
@@ -13,3 +13,9 @@ def init_error_views(app):
|
||||
@app.errorhandler(500)
|
||||
def internal_server_error(e):
|
||||
return render_template('errors/500.html'), 500
|
||||
|
||||
@app.errorhandler(429)
|
||||
def ratelimit_exceeded(e):
|
||||
return jsonify(
|
||||
success=False, msg="请求频率超限,请稍后再试。"
|
||||
)
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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():
|
||||
# 获取请求参数
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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():
|
||||
# 获取请求参数
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>用户管理</title>
|
||||
<title>角色管理</title>
|
||||
{% include 'system/common/header.html' %}
|
||||
</head>
|
||||
<body class="pear-container">
|
||||
|
||||
Reference in New Issue
Block a user