fix: auth 畸形body防御/字段校验信封/用户名校验/429 测试
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import re
|
||||
from django.contrib.auth import get_user_model
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
@@ -5,6 +6,9 @@ from rest_framework.permissions import AllowAny, IsAdminUser
|
||||
from rest_framework.throttling import ScopedRateThrottle
|
||||
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
|
||||
|
||||
# 对齐 Django UnicodeUsernameValidator(\w 默认 Unicode)——create_user 不跑校验器,入口自防
|
||||
_USERNAME_RE = re.compile(r"[\w.@+-]+")
|
||||
|
||||
|
||||
def _ok(data, message="ok"):
|
||||
return Response({"code": 200, "data": data, "message": message})
|
||||
@@ -20,9 +24,13 @@ class _EnvelopeMixin:
|
||||
|
||||
def finalize_response(self, request, response, *args, **kwargs):
|
||||
response = super().finalize_response(request, response, *args, **kwargs)
|
||||
if response.status_code >= 400 and isinstance(response.data, dict) and "detail" in response.data:
|
||||
response.data = {"code": response.status_code, "data": None,
|
||||
"message": response.data["detail"]}
|
||||
if response.status_code >= 400 and isinstance(response.data, dict):
|
||||
if "detail" in response.data:
|
||||
response.data = {"code": response.status_code, "data": None,
|
||||
"message": response.data["detail"]}
|
||||
elif "code" not in response.data: # simplejwt 序列化器字段错误 {"password": [...]} 等
|
||||
response.data = {"code": response.status_code, "data": response.data,
|
||||
"message": "invalid_input"}
|
||||
return response
|
||||
|
||||
|
||||
@@ -59,11 +67,14 @@ class UserCreateView(_EnvelopeMixin, APIView):
|
||||
permission_classes = [IsAdminUser] # is_staff
|
||||
|
||||
def post(self, request):
|
||||
username = (request.data.get("username") or "").strip()
|
||||
password = request.data.get("password") or ""
|
||||
# 用户名长度上限对齐 Django User.username(max_length=150)——create_user 不跑
|
||||
# full_clean,超长会直奔 MySQL DataError 500,故入口拦截
|
||||
if not username or len(username) > 150 or len(password) < 8:
|
||||
data = request.data if isinstance(request.data, dict) else {} # JSON 数组等畸形 body → 空表(.get 会 500)
|
||||
username, password = data.get("username"), data.get("password")
|
||||
if not isinstance(username, str) or not isinstance(password, str): # 123.strip() / len(None) 会 500
|
||||
return Response({"code": 400, "data": None, "message": "invalid_input"}, status=400)
|
||||
username = username.strip()
|
||||
# 字符集对齐 UnicodeUsernameValidator(create_user 不跑校验器);长度上限防 MySQL DataError(sqlite 不报错)
|
||||
if (not username or len(username) > 150 or not _USERNAME_RE.fullmatch(username)
|
||||
or len(password) < 8):
|
||||
return Response({"code": 400, "data": None, "message": "invalid_input"}, status=400)
|
||||
User = get_user_model()
|
||||
if User.objects.filter(username=username).exists():
|
||||
|
||||
Reference in New Issue
Block a user