fix: JWT 用户缓存避免每请求查库 + 移除全 API 限流

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-09 05:00:13 +08:00
parent 1f2a7a32af
commit 5d20c1ed97
3 changed files with 43 additions and 12 deletions

View File

@@ -191,10 +191,7 @@ REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ( 'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated', 'rest_framework.permissions.IsAuthenticated',
), ),
'DEFAULT_THROTTLE_CLASSES': [ # 全局限流会作用于每个 API登录/支付等敏感接口在各自 view 上单独配置了 throttle_classes
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle',
],
'DEFAULT_THROTTLE_RATES': { 'DEFAULT_THROTTLE_RATES': {
'anon': '100/minute', 'anon': '100/minute',
'user': '1000/minute', 'user': '1000/minute',

32
deploy/verify-status.sh Normal file
View File

@@ -0,0 +1,32 @@
#!/bin/bash
# 只读:汇总已排查项,输出结论
set -e
DJANGO_DIR="/opt/1panel/apps/openresty/nginx/www/sites/43.142.166.152.443/django"
LOG="/opt/1panel/apps/openresty/nginx/log/access.log"
cd "$DJANGO_DIR"
echo "=== 1. 数据库地址 ==="
venv/bin/python -c "import app_secrets; print(app_secrets.DATABASE_HOST, app_secrets.DATABASE_PORT)"
echo ""
echo "=== 2. Redis 延迟 ==="
venv/bin/python manage.py shell -c "
import time
from django.core.cache import cache
t=time.perf_counter()
cache.set('ping',1,10); cache.get('ping')
print(round((time.perf_counter()-t)*1000), 'ms')
"
echo ""
echo "=== 3. 最近429限流条数 ==="
grep -a 'hqhd' "$LOG" 2>/dev/null | awk '$9==429' | wc -l | xargs echo "429 count:"
echo ""
echo "=== 4. 最近20条 hqhd rt= ==="
grep -a 'hqhd' "$LOG" 2>/dev/null | tail -20 | grep -oP 'rt=\K[0-9.]+|POST \K[^ ]+' | paste - - | head -10
echo ""
echo "=== 5. Gunicorn ==="
systemctl is-active gunicorn-dianjing.service
pgrep -cf 'gunicorn.*a_long_dianjing' || true

View File

@@ -1,5 +1,6 @@
"""自定义 JWT 认证 — 向后兼容旧 token 的 user_id claim""" """自定义 JWT 认证 — 向后兼容旧 token 的 user_id claim"""
from django.core.cache import cache
from rest_framework_simplejwt.authentication import JWTAuthentication from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.exceptions import InvalidToken, AuthenticationFailed from rest_framework_simplejwt.exceptions import InvalidToken, AuthenticationFailed
from rest_framework_simplejwt.settings import api_settings from rest_framework_simplejwt.settings import api_settings
@@ -16,28 +17,29 @@ class CompatibleJWTAuthentication(JWTAuthentication):
值为 user.UserUID7位业务ID 值为 user.UserUID7位业务ID
此类先尝试新 claim找不到时回退到旧 claim并用对应字段查库。 此类先尝试新 claim找不到时回退到旧 claim并用对应字段查库。
用户对象缓存 300 秒Redis避免每个 API 都打一次 MySQL。
""" """
def get_user(self, validated_token): def get_user(self, validated_token):
# 1. 先尝试当前配置的 USER_ID_CLAIM
user_id = validated_token.get(api_settings.USER_ID_CLAIM) user_id = validated_token.get(api_settings.USER_ID_CLAIM)
lookup_field = api_settings.USER_ID_FIELD lookup_field = api_settings.USER_ID_FIELD
if user_id is None: if user_id is None:
# 2. 回退:尝试旧 token 中的 'user_id' claim
user_id = validated_token.get('user_id') user_id = validated_token.get('user_id')
if user_id is None: if user_id is None:
raise InvalidToken( raise InvalidToken(
{'detail': _('令牌未包含用户标识符'), 'code': 'token_not_valid'} {'detail': _('令牌未包含用户标识符'), 'code': 'token_not_valid'}
) )
# 旧 token 的 user_id 来自 user.id即 UserUUID
# 需要用 UserUUID 字段查库
lookup_field = 'UserUUID' lookup_field = 'UserUUID'
try: cache_key = f'jwt_user:{lookup_field}:{user_id}'
user = self.user_model.objects.get(**{lookup_field: user_id}) user = cache.get(cache_key)
except self.user_model.DoesNotExist: if user is None:
raise AuthenticationFailed(_('用户不存在'), code='user_not_found') try:
user = self.user_model.objects.get(**{lookup_field: user_id})
except self.user_model.DoesNotExist:
raise AuthenticationFailed(_('用户不存在'), code='user_not_found')
cache.set(cache_key, user, 300)
if not user.is_active: if not user.is_active:
raise AuthenticationFailed(_('用户已被禁用'), code='user_inactive') raise AuthenticationFailed(_('用户已被禁用'), code='user_inactive')