优化更新
This commit is contained in:
@@ -471,37 +471,73 @@ def permission_required(permission: str):
|
||||
|
||||
### 权限缓存
|
||||
|
||||
权限缓存采用「用户级缓存 + 全局版本号」双层策略(`src/utils/perm_cache.py`):
|
||||
|
||||
```python
|
||||
# src/modules/system/menu/service.py
|
||||
async def get_permissions_list(user_id: int) -> list:
|
||||
"""获取用户权限列表(异步,走 Redis 缓存)"""
|
||||
cache_key = f"perm:user:{user_id}"
|
||||
# src/utils/perm_cache.py
|
||||
_VERSION_KEY = 'perm:version' # 全局版本号
|
||||
_PERM_PREFIX = 'perm:list:' # 用户权限列表前缀
|
||||
_MENU_PREFIX = 'perm:menu:' # 用户菜单树前缀
|
||||
_CACHE_TTL = 7200 # 缓存过期时间(2小时)
|
||||
|
||||
async def get_cached_permissions(user_id: int):
|
||||
"""获取缓存的用户权限列表,未命中返回 None"""
|
||||
redis = get_redis()
|
||||
cached = await redis.get(cache_key)
|
||||
if cached:
|
||||
return json.loads(cached)
|
||||
# 从数据库查询
|
||||
permissions = query_user_permissions(user_id)
|
||||
await redis.set(cache_key, json.dumps(permissions), ex=3600)
|
||||
return permissions
|
||||
|
||||
|
||||
def get_permissions_list_sync(user_id: int) -> list:
|
||||
"""获取用户权限列表(同步,直查DB,供线程池场景使用)"""
|
||||
return query_user_permissions(user_id)
|
||||
|
||||
|
||||
def invalidate_perm_cache(user_id: int):
|
||||
"""失效用户权限缓存(角色/菜单变更时调用)"""
|
||||
from core.redis_client import get_redis
|
||||
import asyncio
|
||||
ver = await _get_version()
|
||||
key = f'{_PERM_PREFIX}{user_id}:{ver}'
|
||||
data = await redis.get(key)
|
||||
if data is not None:
|
||||
return json.loads(data)
|
||||
return None
|
||||
|
||||
async def set_cached_permissions(user_id: int, perm_list: list):
|
||||
"""缓存用户权限列表(空列表不缓存,避免缓存空壳后无法感知后续数据变更)"""
|
||||
if not perm_list:
|
||||
return
|
||||
redis = get_redis()
|
||||
asyncio.get_event_loop().run_until_complete(
|
||||
redis.delete(f"perm:user:{user_id}")
|
||||
)
|
||||
ver = await _get_version()
|
||||
key = f'{_PERM_PREFIX}{user_id}:{ver}'
|
||||
await redis.set(key, json.dumps(perm_list), ex=_CACHE_TTL)
|
||||
```
|
||||
|
||||
缓存失效策略:
|
||||
|
||||
```python
|
||||
# src/utils/perm_cache.py
|
||||
async def invalidate_menu_change():
|
||||
"""菜单增删改时调用,递增全局版本号使所有用户缓存自然失效"""
|
||||
await _incr_version()
|
||||
|
||||
async def invalidate_user_perms(user_id: int):
|
||||
"""用户角色变更时调用,删除该用户的权限和菜单缓存"""
|
||||
redis = get_redis()
|
||||
ver = await _get_version()
|
||||
keys = [
|
||||
f'{_PERM_PREFIX}{user_id}:{ver}',
|
||||
f'{_MENU_PREFIX}{user_id}:{ver}',
|
||||
]
|
||||
await redis.delete(*keys)
|
||||
|
||||
async def invalidate_role_perms(role_id: int):
|
||||
"""角色菜单变更时调用,清理该角色下所有用户的权限和菜单缓存"""
|
||||
# 查询该角色下所有用户 ID
|
||||
user_ids = await asyncio.to_thread(_query_user_ids, role_id)
|
||||
if not user_ids:
|
||||
return
|
||||
redis = get_redis()
|
||||
ver = await _get_version()
|
||||
keys = []
|
||||
for uid in user_ids:
|
||||
keys.append(f'{_PERM_PREFIX}{uid}:{ver}')
|
||||
keys.append(f'{_MENU_PREFIX}{uid}:{ver}')
|
||||
if keys:
|
||||
await redis.delete(*keys)
|
||||
```
|
||||
|
||||
:::tip 版本号策略的优势
|
||||
传统方案在菜单变更时需要遍历所有用户删除缓存,版本号方案只需递增一个全局 key,所有用户的旧版本缓存自然失效(key 中包含版本号,读取时用新版本号拼 key,旧 key 不再被访问,等 TTL 过期自动清除)。
|
||||
:::
|
||||
|
||||
### check_demo 装饰器
|
||||
|
||||
```python
|
||||
@@ -600,7 +636,7 @@ def sanitize_html(content: str) -> str:
|
||||
### 上传体积限制
|
||||
|
||||
```python
|
||||
# src/middleware/upload_size.py
|
||||
# src/middleware/upload_size_limit.py
|
||||
async def upload_size_limit_middleware(request: Request, call_next):
|
||||
content_length = request.headers.get('content-length')
|
||||
if content_length and int(content_length) > MAX_UPLOAD_SIZE:
|
||||
@@ -695,7 +731,7 @@ async def operation_log_middleware(request: Request, call_next):
|
||||
| 配置项 | 环境变量 | 默认值 | 说明 |
|
||||
|:---|:---|:---|:---|
|
||||
| JWT 密钥 | `JWT_SALT` | 必须配置 | HS256 签名密钥 |
|
||||
| JWT 过期时间 | `JWT_EXPIRE_MINUTES` | 120 | Token 有效期(分钟) |
|
||||
| JWT 过期时间 | `JWT_EXPIRE_MINUTES` | 20 | Token 有效期(分钟) |
|
||||
| 密码错误锁定 | — | 5 次/10 分钟 | 连续失败后锁定 |
|
||||
| 验证码有效期 | — | 120 秒 | 验证码 Redis TTL |
|
||||
| 限流开关 | `RATE_LIMIT_ENABLED` | False | 滑动窗口限流 |
|
||||
|
||||
Reference in New Issue
Block a user