fix(security): P1/P2 审计修复 + JWT HttpOnly Cookie 双模认证 + 限流
- P0/P1 审计修复: 滑块验证码不再下发 x_position/成败即销毁 key、 user-login 补失败计数+滑块门控、限流标识改 X-Real-IP、 百度翻译 appkey 环境化、ChangeEmail/ChangePhone 补调 avalidate、 logs/tasks.py Count(filter=Q) 修复、chat 收藏 SSRF 内网黑名单 - P1 #6/7: token_blacklist + ROTATE_REFRESH_TOKENS 开启, /user/token/refresh/ 挂载 - #2 JWT HttpOnly Cookie 双模认证: user/cookie_auth.py 种/清 Cookie, user/authentication.py CookieOrHeaderJWTAuthentication(Bearer 优先/_COOKIE 兜底), user/views/token.py CookieTokenRefreshView + UserLogoutAPIView(/user/logout/), create_standardized_response 自动对含 token 的响应种 Cookie, 异步视图内 RefreshToken.for_user 全部 sync_to_async 包裹(修 SynchronousOnlyOperation 500), WS ChatConsumer 优先读 Cookie token - P2 #11 限流: utils/rate_limit.py 固定窗口频控, shorturl 生成 匿名10次/分+登录60次/分, 邮箱验证码 同邮箱60s1次+同IP10次/10min, 登录/注册验证码 错5次作废+成功即销毁防重放, 换绑邮箱/手机 同步落地, urls.py 补挂 shorturl 路由(此前 404)
This commit is contained in:
+21
-3
@@ -6,9 +6,11 @@ from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||
from adrf.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from asgiref.sync import sync_to_async
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from drf_yasg import openapi
|
||||
from chunyu_project.common_schemas import success_response, error_response, unauthorized_response
|
||||
from utils.rate_limit import check_rate_limit, get_client_ip
|
||||
|
||||
from .models import ShortUrl
|
||||
|
||||
@@ -55,6 +57,16 @@ class ShortUrlShortenView(APIView):
|
||||
responses={200: success_response, 400: error_response, 409: error_response}
|
||||
)
|
||||
async def post(self, request):
|
||||
# 频控加固:未登录单 IP 每分钟限 10 次,已登录限 60 次
|
||||
is_auth = request.user and request.user.is_authenticated
|
||||
identifier = str(request.user.id) if is_auth else get_client_ip(request)
|
||||
limit = 60 if is_auth else 10
|
||||
if identifier and not await sync_to_async(check_rate_limit)('shorturl_shorten', identifier, limit=limit, window_seconds=60):
|
||||
return Response(
|
||||
{"code": 429, "message": "生成短链接过于频繁,请稍后再试"},
|
||||
status=status.HTTP_429_TOO_MANY_REQUESTS
|
||||
)
|
||||
|
||||
url = request.data.get('url', '').strip()
|
||||
custom_code = request.data.get('custom_code', '').strip() or None
|
||||
expire_days = request.data.get('expire_days')
|
||||
@@ -183,9 +195,15 @@ class ShortUrlListView(APIView):
|
||||
async def get(self, request):
|
||||
queryset = ShortUrl.objects.filter(creator=request.user).order_by('-created_at')
|
||||
|
||||
page = int(request.GET.get('page', 1))
|
||||
page_size = int(request.GET.get('page_size', 20))
|
||||
page_size = min(page_size, 100)
|
||||
try:
|
||||
page = max(1, int(request.GET.get('page', 1)))
|
||||
except (ValueError, TypeError):
|
||||
page = 1
|
||||
|
||||
try:
|
||||
page_size = max(1, min(int(request.GET.get('page_size', 20)), 100))
|
||||
except (ValueError, TypeError):
|
||||
page_size = 20
|
||||
|
||||
start = (page - 1) * page_size
|
||||
end = start + page_size
|
||||
|
||||
Reference in New Issue
Block a user