fix: JWT 用户缓存避免每请求查库 + 移除全 API 限流
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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
32
deploy/verify-status.sh
Normal 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
|
||||||
@@ -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.UserUID(7位业务ID)。
|
值为 user.UserUID(7位业务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')
|
||||||
|
|||||||
Reference in New Issue
Block a user