- 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)
132 lines
4.7 KiB
Python
132 lines
4.7 KiB
Python
"""
|
|
Response codes and messages enumeration for standardized API responses
|
|
|
|
This module contains all response codes and messages used across the application.
|
|
Codes are 5-digit numbers:
|
|
- 10000-19999: Success codes
|
|
- 20000-29999: Error codes
|
|
"""
|
|
|
|
from enum import IntEnum, Enum
|
|
|
|
|
|
class ResponseCode(IntEnum):
|
|
"""Response codes enumeration (5-digit numbers)"""
|
|
|
|
SUCCESS = 10000
|
|
EMAIL_SENT_REGISTER = 10001
|
|
EMAIL_SENT_LOGIN = 10002
|
|
REGISTRATION_SUCCESS = 10003
|
|
LOGIN_SUCCESS = 10004
|
|
PASSWORD_CHANGED = 10005
|
|
PASSWORD_SET = 10006
|
|
EMAIL_CHANGE_CODE_SENT = 10007
|
|
EMAIL_CHANGED = 10008
|
|
PARAMETER_ERROR = 20001
|
|
EMAIL_EMPTY = 20002
|
|
EMAIL_SEND_FAILED = 20003
|
|
VERIFICATION_CODE_EXPIRED = 20004
|
|
USER_DATA_INVALID = 20005
|
|
VERIFICATION_CODE_ERROR = 20006
|
|
LOGIN_VERIFICATION_EXPIRED = 20007
|
|
LOGIN_VERIFICATION_ERROR = 20008
|
|
SERVER_INTERNAL_ERROR = 20009
|
|
OLD_PASSWORD_ERROR = 20010
|
|
PASSWORD_MISMATCH = 20011
|
|
PASSWORD_TOO_SHORT = 20012
|
|
PASSWORD_TOO_WEAK = 20013
|
|
EMAIL_SAME_AS_OLD = 20014
|
|
EMAIL_ALREADY_IN_USE = 20015
|
|
RESET_CODE_SENT = 10009
|
|
PASSWORD_RESET_SUCCESS = 10010
|
|
VALIDATION_ERROR = 20016
|
|
NOT_FOUND = 20017
|
|
USER_NOT_FOUND = 20018
|
|
CAPTCHA_ERROR = 20020
|
|
CAPTCHA_EXPIRED = 20021
|
|
CAPTCHA_REQUIRED = 20022
|
|
EMAIL_DOMAIN_INVALID = 20023
|
|
|
|
|
|
class ResponseMessage:
|
|
"""Response messages enumeration (static class)"""
|
|
|
|
SUCCESS = 'Success'
|
|
EMAIL_SENT_REGISTER = '账号未注册,已发送注册邮件'
|
|
EMAIL_SENT_LOGIN = '邮件发送成功'
|
|
REGISTRATION_SUCCESS = '注册成功'
|
|
LOGIN_SUCCESS = '登录成功'
|
|
PASSWORD_CHANGED = '密码修改成功'
|
|
PASSWORD_SET = '密码设置成功'
|
|
EMAIL_CHANGE_CODE_SENT = '验证码已发送至邮箱'
|
|
EMAIL_CHANGED = '邮箱修改成功'
|
|
PARAMETER_ERROR = '参数异常'
|
|
EMAIL_EMPTY = '邮箱地址不能为空'
|
|
EMAIL_SEND_FAILED = '邮件发送失败'
|
|
VERIFICATION_CODE_EXPIRED = '验证码已过期或未发送'
|
|
USER_DATA_INVALID = '用户数据验证失败'
|
|
VERIFICATION_CODE_ERROR = '验证码错误'
|
|
LOGIN_VERIFICATION_EXPIRED = '验证码已过期或未发送'
|
|
LOGIN_VERIFICATION_ERROR = '验证码错误'
|
|
SERVER_INTERNAL_ERROR = '服务器内部错误'
|
|
OLD_PASSWORD_ERROR = '当前密码错误'
|
|
PASSWORD_MISMATCH = '两次输入的新密码不一致'
|
|
PASSWORD_TOO_SHORT = '密码长度不能少于8位'
|
|
PASSWORD_TOO_WEAK = '密码必须包含字母和数字'
|
|
EMAIL_SAME_AS_OLD = '新邮箱与当前邮箱相同'
|
|
EMAIL_ALREADY_IN_USE = '该邮箱已被其他账号使用'
|
|
RESET_CODE_SENT = '重置密码验证码已发送至邮箱'
|
|
PASSWORD_RESET_SUCCESS = '密码重置成功'
|
|
VALIDATION_ERROR = '数据验证失败'
|
|
NOT_FOUND = '资源不存在'
|
|
USER_NOT_FOUND = '该邮箱未注册'
|
|
CAPTCHA_ERROR = '验证码错误'
|
|
CAPTCHA_EXPIRED = '验证码已过期,请重新获取'
|
|
CAPTCHA_REQUIRED = '操作过于频繁,请输入验证码'
|
|
EMAIL_DOMAIN_INVALID = '邮箱域名不存在,请输入真实邮箱地址'
|
|
|
|
|
|
def get_response_message(code: ResponseCode) -> str:
|
|
"""Get the corresponding message for a response code."""
|
|
return getattr(ResponseMessage, code.name, str(code))
|
|
|
|
|
|
SUCCESS = 200
|
|
SERVER_INTERNAL_ERROR = 500
|
|
|
|
|
|
def create_standardized_response(data=None, code=None, status_code=200, message=None):
|
|
"""Create a standardized success response."""
|
|
from rest_framework.response import Response
|
|
response_data = {}
|
|
if data is not None:
|
|
response_data['data'] = data
|
|
if code is not None:
|
|
response_data['code'] = int(code)
|
|
response_data['message'] = message or get_response_message(code)
|
|
response = Response(response_data, status=status_code)
|
|
# 安全加固:若返回数据含 access 或 refresh,自动植入 HttpOnly Cookie 杜绝 XSS 窃取
|
|
if isinstance(data, dict):
|
|
access = data.get('access')
|
|
refresh = data.get('refresh')
|
|
if access or refresh:
|
|
try:
|
|
from user.cookie_auth import set_auth_cookies
|
|
set_auth_cookies(response, access_token=access, refresh_token=refresh)
|
|
except Exception:
|
|
pass
|
|
return response
|
|
|
|
|
|
def create_standardized_error_response(data=None, code=None, status_code=400, message=None):
|
|
"""Create a standardized error response."""
|
|
from rest_framework.response import Response
|
|
response_data = {}
|
|
if data is not None:
|
|
response_data['data'] = data
|
|
if code is not None:
|
|
response_data['code'] = int(code)
|
|
response_data['message'] = message or get_response_message(code)
|
|
else:
|
|
response_data['message'] = message or 'Unknown error'
|
|
return Response(response_data, status=status_code) |