引入接口频率限制机制
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_upload import init_upload
|
||||||
from .init_migrate import init_migrate
|
from .init_migrate import init_migrate
|
||||||
from .init_session import init_session
|
from .init_session import init_session
|
||||||
|
from .init_limit import init_limit
|
||||||
from .init_plugins import register_plugin, broadcast_execute
|
from .init_plugins import register_plugin, broadcast_execute
|
||||||
|
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ def init_plugs(app: Flask) -> None:
|
|||||||
init_upload(app)
|
init_upload(app)
|
||||||
init_migrate(app)
|
init_migrate(app)
|
||||||
init_session(app)
|
init_session(app)
|
||||||
|
init_limit(app)
|
||||||
|
|
||||||
# 系统蓝图相关
|
# 系统蓝图相关
|
||||||
init_template_directives(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):
|
def init_error_views(app):
|
||||||
@@ -12,4 +12,10 @@ def init_error_views(app):
|
|||||||
|
|
||||||
@app.errorhandler(500)
|
@app.errorhandler(500)
|
||||||
def internal_server_error(e):
|
def internal_server_error(e):
|
||||||
return render_template('errors/500.html'), 500
|
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.rights import authorize
|
||||||
from applications.common.utils.validate import str_escape
|
from applications.common.utils.validate import str_escape
|
||||||
from applications.extensions import db
|
from applications.extensions import db
|
||||||
|
from applications.extensions.init_limit import limiter
|
||||||
from applications.models import Dept, User
|
from applications.models import Dept, User
|
||||||
from applications.schemas import DeptSchema
|
from applications.schemas import DeptSchema
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
@bp.post('/data')
|
@bp.post('/data')
|
||||||
|
@limiter.limit("60 per minute")
|
||||||
@authorize("system:dept:main", log=True)
|
@authorize("system:dept:main", log=True)
|
||||||
def data():
|
def data():
|
||||||
dept = Dept.query.order_by(Dept.sort).all()
|
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.rights import authorize
|
||||||
from applications.common.utils.validate import str_escape
|
from applications.common.utils.validate import str_escape
|
||||||
from applications.extensions import db
|
from applications.extensions import db
|
||||||
|
from applications.extensions.init_limit import limiter
|
||||||
from applications.models import DictType, DictData
|
from applications.models import DictType, DictData
|
||||||
from applications.schemas import DictTypeOutSchema, DictDataOutSchema
|
from applications.schemas import DictTypeOutSchema, DictDataOutSchema
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
@bp.get('/dictType/data')
|
@bp.get('/dictType/data')
|
||||||
|
@limiter.limit("60 per minute")
|
||||||
@authorize("system:dict:main")
|
@authorize("system:dict:main")
|
||||||
def dict_type_data():
|
def dict_type_data():
|
||||||
# 获取请求参数
|
# 获取请求参数
|
||||||
@@ -154,6 +156,7 @@ def dict_type_delete(_id):
|
|||||||
|
|
||||||
|
|
||||||
@bp.get('/dictData/data')
|
@bp.get('/dictData/data')
|
||||||
|
@limiter.limit("60 per minute")
|
||||||
@authorize("system:dict:main", log=True)
|
@authorize("system:dict:main", log=True)
|
||||||
def dict_code_data():
|
def dict_code_data():
|
||||||
type_code = str_escape(request.args.get('typeCode', type=str))
|
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.http import fail_api, success_api, table_api
|
||||||
from applications.common.utils.rights import authorize
|
from applications.common.utils.rights import authorize
|
||||||
from applications.extensions import db
|
from applications.extensions import db
|
||||||
|
from applications.extensions.init_limit import limiter
|
||||||
from applications.models import Photo
|
from applications.models import Photo
|
||||||
from applications.common.utils import upload as upload_curd
|
from applications.common.utils import upload as upload_curd
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ def index():
|
|||||||
|
|
||||||
# 图片数据
|
# 图片数据
|
||||||
@bp.get('/table')
|
@bp.get('/table')
|
||||||
|
@limiter.limit("60 per minute")
|
||||||
@authorize("system:file:main")
|
@authorize("system:file:main")
|
||||||
def table():
|
def table():
|
||||||
page = request.args.get('page', type=int)
|
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.rights import authorize
|
||||||
from applications.common.utils.validate import str_escape
|
from applications.common.utils.validate import str_escape
|
||||||
from applications.extensions import db
|
from applications.extensions import db
|
||||||
|
from applications.extensions.init_limit import limiter
|
||||||
from applications.models import Mail
|
from applications.models import Mail
|
||||||
from applications.schemas import MailOutSchema
|
from applications.schemas import MailOutSchema
|
||||||
from applications.common.utils import mail
|
from applications.common.utils import mail
|
||||||
@@ -24,6 +25,7 @@ def main():
|
|||||||
|
|
||||||
# 用户分页查询
|
# 用户分页查询
|
||||||
@bp.get('/data')
|
@bp.get('/data')
|
||||||
|
@limiter.limit("60 per minute")
|
||||||
@authorize("system:mail:main")
|
@authorize("system:mail:main")
|
||||||
def data():
|
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.rights import authorize
|
||||||
from applications.common.utils.validate import str_escape
|
from applications.common.utils.validate import str_escape
|
||||||
from applications.extensions import db
|
from applications.extensions import db
|
||||||
|
from applications.extensions.init_limit import limiter
|
||||||
from applications.models import Power
|
from applications.models import Power
|
||||||
from applications.schemas import PowerOutSchema2
|
from applications.schemas import PowerOutSchema2
|
||||||
from applications.schemas.admin_power import PowerSchema
|
from applications.schemas.admin_power import PowerSchema
|
||||||
@@ -19,6 +20,7 @@ def index():
|
|||||||
|
|
||||||
|
|
||||||
@bp.post('/data')
|
@bp.post('/data')
|
||||||
|
@limiter.limit("60 per minute")
|
||||||
@authorize("system:power:main")
|
@authorize("system:power:main")
|
||||||
def data():
|
def data():
|
||||||
power = Power.query.all()
|
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.rights import authorize
|
||||||
from applications.common.utils.validate import str_escape
|
from applications.common.utils.validate import str_escape
|
||||||
from applications.extensions import db
|
from applications.extensions import db
|
||||||
|
from applications.extensions.init_limit import limiter
|
||||||
from applications.models import Role, Power, User
|
from applications.models import Role, Power, User
|
||||||
from applications.schemas import RoleOutSchema, PowerOutSchema2
|
from applications.schemas import RoleOutSchema, PowerOutSchema2
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ def main():
|
|||||||
|
|
||||||
# 表格数据
|
# 表格数据
|
||||||
@bp.get('/data')
|
@bp.get('/data')
|
||||||
|
@limiter.limit("60 per minute")
|
||||||
@authorize("system:role:main")
|
@authorize("system:role:main")
|
||||||
def table():
|
def table():
|
||||||
role_name = str_escape(request.args.get('roleName', type=str))
|
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.rights import authorize
|
||||||
from applications.common.utils.validate import str_escape
|
from applications.common.utils.validate import str_escape
|
||||||
from applications.extensions import db
|
from applications.extensions import db
|
||||||
|
from applications.extensions.init_limit import limiter
|
||||||
from applications.models import Role, Dept
|
from applications.models import Role, Dept
|
||||||
from applications.models import User, AdminLog
|
from applications.models import User, AdminLog
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ def main():
|
|||||||
|
|
||||||
# 用户分页查询
|
# 用户分页查询
|
||||||
@bp.get('/data')
|
@bp.get('/data')
|
||||||
|
@limiter.limit("60 per minute")
|
||||||
@authorize("system:user:main")
|
@authorize("system:user:main")
|
||||||
def data():
|
def data():
|
||||||
# 获取请求参数
|
# 获取请求参数
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>用户管理</title>
|
<title>角色管理</title>
|
||||||
{% include 'system/common/header.html' %}
|
{% include 'system/common/header.html' %}
|
||||||
</head>
|
</head>
|
||||||
<body class="pear-container">
|
<body class="pear-container">
|
||||||
|
|||||||
Reference in New Issue
Block a user