⚒️ 重大重构 LoveACE V2
引入了 mongodb 对数据库进行了一定程度的数据加密 性能改善 代码简化 统一错误模型和响应 使用 apifox 作为文档
This commit is contained in:
10
loveace/router/endpoint/auth/__init__.py
Normal file
10
loveace/router/endpoint/auth/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from loveace.router.endpoint.auth.authme import authme_router
|
||||
from loveace.router.endpoint.auth.login import login_router
|
||||
from loveace.router.endpoint.auth.register import register_router
|
||||
|
||||
auth_router = APIRouter(prefix="/auth", tags=["用户验证"])
|
||||
auth_router.include_router(login_router)
|
||||
auth_router.include_router(register_router)
|
||||
auth_router.include_router(authme_router)
|
||||
45
loveace/router/endpoint/auth/authme.py
Normal file
45
loveace/router/endpoint/auth/authme.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from loveace.router.endpoint.auth.model.authme import AuthMeResponse
|
||||
from loveace.router.schemas.error import ProtectRouterErrorToCode
|
||||
from loveace.router.schemas.uniresponse import UniResponseModel
|
||||
from loveace.service.remote.aufe import AUFEConnection
|
||||
from loveace.service.remote.aufe.depends import get_aufe_conn
|
||||
|
||||
authme_router = APIRouter(
|
||||
prefix="/authme", responses=ProtectRouterErrorToCode.gen_code_table()
|
||||
)
|
||||
|
||||
|
||||
@authme_router.get(
|
||||
"/token",
|
||||
response_model=UniResponseModel[AuthMeResponse],
|
||||
summary="Token 有效性验证",
|
||||
)
|
||||
async def auth_me(
|
||||
conn: AUFEConnection = Depends(get_aufe_conn),
|
||||
) -> UniResponseModel[AuthMeResponse] | JSONResponse:
|
||||
"""
|
||||
验证 Token 有效性并获取用户信息
|
||||
|
||||
✅ 功能特性:
|
||||
- 验证 Authme Token 是否有效
|
||||
- 返回当前认证用户的 ID
|
||||
- 用于前端权限验证
|
||||
|
||||
💡 使用场景:
|
||||
- 前端页面加载时验证登录状态
|
||||
- Token 过期检测
|
||||
- 获取当前登录用户信息
|
||||
|
||||
Returns:
|
||||
AuthMeResponse: 包含验证结果和用户 ID
|
||||
"""
|
||||
user_id = conn.userid
|
||||
return UniResponseModel[AuthMeResponse](
|
||||
success=True,
|
||||
data=AuthMeResponse(success=True, userid=user_id),
|
||||
message="Token 验证成功",
|
||||
error=None,
|
||||
)
|
||||
222
loveace/router/endpoint/auth/login.py
Normal file
222
loveace/router/endpoint/auth/login.py
Normal file
@@ -0,0 +1,222 @@
|
||||
import secrets
|
||||
from datetime import datetime, timedelta
|
||||
from uuid import uuid4
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi.responses import JSONResponse
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from loveace.config.logger import LoggerMixin
|
||||
from loveace.database.auth.login import LoginCoolDown
|
||||
from loveace.database.auth.token import AuthMEToken
|
||||
from loveace.database.auth.user import ACEUser
|
||||
from loveace.database.creator import get_db_session
|
||||
from loveace.router.dependencies.logger import no_user_logger_mixin
|
||||
from loveace.router.endpoint.auth.model.login import (
|
||||
LoginErrorToCode,
|
||||
LoginRequest,
|
||||
LoginResponse,
|
||||
)
|
||||
from loveace.router.schemas.uniresponse import UniResponseModel
|
||||
from loveace.service.remote.aufe import AUFEService
|
||||
from loveace.service.remote.aufe.depends import get_aufe_service
|
||||
from loveace.utils.rsa import RSAUtils
|
||||
|
||||
login_router = APIRouter(prefix="/login", responses=LoginErrorToCode.gen_code_table())
|
||||
rsa_util = RSAUtils.get_or_create_rsa_utils()
|
||||
|
||||
|
||||
@login_router.post(
|
||||
"/next",
|
||||
response_model=UniResponseModel[LoginResponse],
|
||||
summary="用户登录",
|
||||
)
|
||||
async def login(
|
||||
login_request: LoginRequest,
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
aufe_service: AUFEService = Depends(get_aufe_service),
|
||||
logger: LoggerMixin = Depends(no_user_logger_mixin),
|
||||
) -> UniResponseModel[LoginResponse] | JSONResponse:
|
||||
"""
|
||||
用户登录,返回 Authme Token
|
||||
|
||||
✅ 功能特性:
|
||||
- 通过 AUFE 服务验证 EC 密码和登录密码
|
||||
- 限制用户总 Token 数为 5 个
|
||||
- 登录失败后设置 1 分钟冷却时间
|
||||
|
||||
⚠️ 限制条件:
|
||||
- 连续登录失败会触发冷却机制
|
||||
- 冷却期间内拒绝该用户的登录请求
|
||||
|
||||
💡 使用场景:
|
||||
- 用户首次登录
|
||||
- 用户重新登录(更换设备)
|
||||
- 用户忘记密码后重新设置并登录
|
||||
|
||||
Args:
|
||||
login_request: 包含用户 ID、EC 密码、登录密码的登录请求
|
||||
db: 数据库会话
|
||||
aufe_service: AUFE 远程认证服务
|
||||
logger: 日志记录器
|
||||
|
||||
Returns:
|
||||
LoginResponse: 包含新生成的 Authme Token
|
||||
"""
|
||||
try:
|
||||
async with db as session:
|
||||
logger.info(f"用户登录: {login_request.userid}")
|
||||
# 检查用户是否存在
|
||||
query = select(ACEUser).where(ACEUser.userid == login_request.userid)
|
||||
result = await session.execute(query)
|
||||
user = result.scalars().first()
|
||||
if user is None:
|
||||
logger.info(f"用户不存在: {login_request.userid}")
|
||||
return LoginErrorToCode().invalid_credentials.to_json_response(
|
||||
logger.trace_id
|
||||
)
|
||||
# 检查是否在冷却时间内
|
||||
query = select(LoginCoolDown).where(LoginCoolDown.userid == user.userid)
|
||||
result = await session.execute(query)
|
||||
cooldown = result.scalars().first()
|
||||
if cooldown and cooldown.expire_date > datetime.now():
|
||||
logger.info(f"用户 {login_request.userid} 在冷却时间内,拒绝登录")
|
||||
return LoginErrorToCode().cooldown.to_json_response(logger.trace_id)
|
||||
# 解密数据库中的 EC密码 登录密码 和 请求体中的 EC密码 登录密码
|
||||
try:
|
||||
db_ec_password = rsa_util.decrypt(user.ec_password)
|
||||
db_password = rsa_util.decrypt(user.password)
|
||||
ec_password = rsa_util.decrypt(login_request.ec_password)
|
||||
password = rsa_util.decrypt(login_request.password)
|
||||
except Exception as e:
|
||||
logger.info(f"用户 {login_request.userid} 提供的密码解密失败: {e}")
|
||||
return LoginErrorToCode().invalid_credentials.to_json_response(
|
||||
logger.trace_id
|
||||
)
|
||||
# 尝试使用AUFE服务验证EC密码和登录密码
|
||||
conn = await aufe_service.get_or_create_connection(
|
||||
userid=login_request.userid,
|
||||
ec_password=ec_password,
|
||||
password=password,
|
||||
)
|
||||
if not await conn.health_check():
|
||||
logger.info(f"用户 {login_request.userid} 的AUFE连接不可用")
|
||||
|
||||
# EC密码登录重试机制 (最多3次)
|
||||
ec_login_status = None
|
||||
for ec_retry in range(3):
|
||||
ec_login_status = await conn.ec_login()
|
||||
if ec_login_status.success:
|
||||
break
|
||||
|
||||
# 如果是攻击防范或密码错误,直接退出重试
|
||||
if (
|
||||
ec_login_status.fail_maybe_attacked
|
||||
or ec_login_status.fail_invalid_credentials
|
||||
):
|
||||
logger.info(
|
||||
f"用户 {login_request.userid} EC登录失败 (攻击防范或密码错误),停止重试"
|
||||
)
|
||||
break
|
||||
|
||||
logger.info(
|
||||
f"用户 {login_request.userid} EC登录重试第 {ec_retry + 1} 次"
|
||||
)
|
||||
|
||||
if not ec_login_status or not ec_login_status.success:
|
||||
logger.info(f"用户 {login_request.userid} 的EC密码错误")
|
||||
# 设置冷却时间
|
||||
cooldown_time = timedelta(minutes=1)
|
||||
if cooldown:
|
||||
cooldown.expire_date = datetime.now() + cooldown_time
|
||||
else:
|
||||
cooldown = LoginCoolDown(
|
||||
userid=user.userid,
|
||||
expire_date=datetime.now() + cooldown_time,
|
||||
)
|
||||
session.add(cooldown)
|
||||
await session.commit()
|
||||
return (
|
||||
LoginErrorToCode().remote_invalid_credentials.to_json_response(
|
||||
logger.trace_id
|
||||
)
|
||||
)
|
||||
|
||||
# UAAP密码登录重试机制 (最多3次)
|
||||
uaap_login_status = None
|
||||
for uaap_retry in range(3):
|
||||
uaap_login_status = await conn.uaap_login()
|
||||
if uaap_login_status.success:
|
||||
break
|
||||
|
||||
# 如果是密码错误,直接退出重试
|
||||
if uaap_login_status.fail_invalid_credentials:
|
||||
logger.info(
|
||||
f"用户 {login_request.userid} UAAP登录失败 (密码错误),停止重试"
|
||||
)
|
||||
break
|
||||
|
||||
logger.info(
|
||||
f"用户 {login_request.userid} UAAP登录重试第 {uaap_retry + 1} 次"
|
||||
)
|
||||
|
||||
if not uaap_login_status or not uaap_login_status.success:
|
||||
logger.info(f"用户 {login_request.userid} 的登录密码错误")
|
||||
# 设置冷却时间
|
||||
cooldown_time = timedelta(minutes=1)
|
||||
if cooldown:
|
||||
cooldown.expire_date = datetime.now() + cooldown_time
|
||||
else:
|
||||
cooldown = LoginCoolDown(
|
||||
userid=user.userid,
|
||||
expire_date=datetime.now() + cooldown_time,
|
||||
)
|
||||
session.add(cooldown)
|
||||
await session.commit()
|
||||
return (
|
||||
LoginErrorToCode().remote_invalid_credentials.to_json_response(
|
||||
logger.trace_id
|
||||
)
|
||||
)
|
||||
# 删除冷却时间
|
||||
if cooldown:
|
||||
await session.delete(cooldown)
|
||||
await session.commit()
|
||||
# 比对密码,如果新的密码与数据库中的密码不一致,则更新数据库中的密码
|
||||
if db_ec_password != ec_password or db_password != password:
|
||||
user.ec_password = rsa_util.encrypt(ec_password)
|
||||
user.password = rsa_util.encrypt(password)
|
||||
session.add(user)
|
||||
await session.commit()
|
||||
logger.info(f"用户 {login_request.userid} 的密码已更新")
|
||||
# 创建新的Authme Token
|
||||
new_token = AuthMEToken(
|
||||
user_id=user.userid,
|
||||
token=secrets.token_urlsafe(32),
|
||||
device_id=uuid4().hex,
|
||||
)
|
||||
session.add(new_token)
|
||||
await session.commit()
|
||||
# 限制用户总 Token 数为5个,删除最早的 Token
|
||||
query = (
|
||||
select(AuthMEToken)
|
||||
.where(AuthMEToken.user_id == user.userid)
|
||||
.order_by(AuthMEToken.create_date.asc())
|
||||
)
|
||||
result = await session.execute(query)
|
||||
tokens = result.scalars().all()
|
||||
if len(tokens) > 5:
|
||||
for token in tokens[:-5]:
|
||||
await session.delete(token)
|
||||
await session.commit()
|
||||
logger.info(f"用户 {login_request.userid} 登录成功,返回Token")
|
||||
return UniResponseModel[LoginResponse](
|
||||
success=True,
|
||||
data=LoginResponse(token=new_token.token),
|
||||
message="登录成功",
|
||||
error=None,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"用户 {login_request.userid} 登录时发生错误: {e}")
|
||||
return LoginErrorToCode().server_error.to_json_response(logger.trace_id)
|
||||
6
loveace/router/endpoint/auth/model/authme.py
Normal file
6
loveace/router/endpoint/auth/model/authme.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class AuthMeResponse(BaseModel):
|
||||
success: bool = Field(..., description="是否验证成功")
|
||||
userid: str = Field(..., description="用户ID")
|
||||
37
loveace/router/endpoint/auth/model/login.py
Normal file
37
loveace/router/endpoint/auth/model/login.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from fastapi import status
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from loveace.router.schemas.base import ErrorToCode, ErrorToCodeNode
|
||||
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
userid: str = Field(..., description="用户ID")
|
||||
ec_password: str = Field(..., description="用户EC密码,rsa encrypt加密后的密文")
|
||||
password: str = Field(..., description="用户登录密码,rsa encrypt加密后的密文")
|
||||
|
||||
|
||||
class LoginResponse(BaseModel):
|
||||
token: str = Field(..., description="用户登录成功后返回的Authme Token")
|
||||
|
||||
|
||||
class LoginErrorToCode(ErrorToCode):
|
||||
invalid_credentials: ErrorToCodeNode = ErrorToCodeNode(
|
||||
error_code=status.HTTP_403_FORBIDDEN,
|
||||
code="CREDENTIALS_INVALID",
|
||||
message="凭证无效",
|
||||
)
|
||||
remote_invalid_credentials: ErrorToCodeNode = ErrorToCodeNode(
|
||||
error_code=status.HTTP_403_FORBIDDEN,
|
||||
code="REMOTE_CREDENTIALS_INVALID",
|
||||
message="远程凭证无效,EC密码或登录密码错误,需要进行密码重置",
|
||||
)
|
||||
cooldown: ErrorToCodeNode = ErrorToCodeNode(
|
||||
error_code=status.HTTP_429_TOO_MANY_REQUESTS,
|
||||
code="COOLDOWN",
|
||||
message="操作过于频繁,请稍后再试",
|
||||
)
|
||||
server_error: ErrorToCodeNode = ErrorToCodeNode(
|
||||
error_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
code="SERVER_ERROR",
|
||||
message="服务器错误",
|
||||
)
|
||||
99
loveace/router/endpoint/auth/model/register.py
Normal file
99
loveace/router/endpoint/auth/model/register.py
Normal file
@@ -0,0 +1,99 @@
|
||||
from fastapi import status
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from loveace.router.schemas import (
|
||||
ErrorToCode,
|
||||
ErrorToCodeNode,
|
||||
)
|
||||
|
||||
##############################################################
|
||||
# * 用户注册相关模型-邀请码 *#
|
||||
|
||||
|
||||
class InviteCodeRequest(BaseModel):
|
||||
invite_code: str = Field(..., description="邀请码")
|
||||
|
||||
|
||||
class InviteCodeResponse(BaseModel):
|
||||
token: str = Field(..., description="邀请码验证成功后返回的Token")
|
||||
|
||||
|
||||
class InviteErrorToCode(ErrorToCode):
|
||||
invalid_invite_code: ErrorToCodeNode = ErrorToCodeNode(
|
||||
error_code=status.HTTP_403_FORBIDDEN,
|
||||
code="INVITE_CODE_INVALID",
|
||||
message="邀请码错误",
|
||||
)
|
||||
server_error: ErrorToCodeNode = ErrorToCodeNode(
|
||||
error_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
code="SERVER_ERROR",
|
||||
message="服务器错误",
|
||||
)
|
||||
|
||||
|
||||
##############################################################
|
||||
|
||||
##############################################################
|
||||
# * 用户注册相关模型-注册 *#
|
||||
|
||||
|
||||
class RegisterRequest(BaseModel):
|
||||
userid: str = Field(..., description="用户ID")
|
||||
ec_password: str = Field(..., description="用户EC密码,rsa encrypt加密后的密文")
|
||||
password: str = Field(..., description="用户登录密码,rsa encrypt加密后的密文")
|
||||
token: str = Field(..., description="邀请码验证成功后返回的Token")
|
||||
|
||||
|
||||
class RegisterResponse(BaseModel):
|
||||
token: str = Field(..., description="用户登录成功后返回的Authme Token")
|
||||
|
||||
|
||||
class RegisterErrorToCode(ErrorToCode):
|
||||
invalid_token: ErrorToCodeNode = ErrorToCodeNode(
|
||||
error_code=status.HTTP_403_FORBIDDEN,
|
||||
code="TOKEN_INVALID",
|
||||
message="Token无效",
|
||||
)
|
||||
userid_exists: ErrorToCodeNode = ErrorToCodeNode(
|
||||
error_code=status.HTTP_409_CONFLICT,
|
||||
code="USERID_EXISTS",
|
||||
message="用户ID已存在",
|
||||
)
|
||||
decrypt_error: ErrorToCodeNode = ErrorToCodeNode(
|
||||
error_code=status.HTTP_400_BAD_REQUEST,
|
||||
code="DECRYPT_ERROR",
|
||||
message="密码解密失败",
|
||||
)
|
||||
ec_server_error: ErrorToCodeNode = ErrorToCodeNode(
|
||||
error_code=status.HTTP_400_BAD_REQUEST,
|
||||
code="EC_SERVER_ERROR",
|
||||
message="EC服务错误",
|
||||
)
|
||||
ec_password_error: ErrorToCodeNode = ErrorToCodeNode(
|
||||
error_code=status.HTTP_400_BAD_REQUEST,
|
||||
code="EC_PASSWORD_ERROR",
|
||||
message="EC密码错误",
|
||||
)
|
||||
uaap_server_error: ErrorToCodeNode = ErrorToCodeNode(
|
||||
error_code=status.HTTP_400_BAD_REQUEST,
|
||||
code="UAAP_SERVER_ERROR",
|
||||
message="UAAP服务错误",
|
||||
)
|
||||
uaap_password_error: ErrorToCodeNode = ErrorToCodeNode(
|
||||
error_code=status.HTTP_400_BAD_REQUEST,
|
||||
code="UAAP_PASSWORD_ERROR",
|
||||
message="UAAP密码错误",
|
||||
)
|
||||
register_in_cooldown: ErrorToCodeNode = ErrorToCodeNode(
|
||||
error_code=status.HTTP_429_TOO_MANY_REQUESTS,
|
||||
code="REGISTER_IN_COOLDOWN",
|
||||
message="注册请求过于频繁,请稍后再试",
|
||||
)
|
||||
server_error: ErrorToCodeNode = ErrorToCodeNode(
|
||||
error_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
code="SERVER_ERROR",
|
||||
message="服务器错误",
|
||||
)
|
||||
|
||||
|
||||
##############################################################
|
||||
247
loveace/router/endpoint/auth/register.py
Normal file
247
loveace/router/endpoint/auth/register.py
Normal file
@@ -0,0 +1,247 @@
|
||||
import secrets
|
||||
from datetime import datetime, timedelta
|
||||
from uuid import uuid4
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi.responses import JSONResponse
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from loveace.config.logger import LoggerMixin
|
||||
from loveace.database.auth.register import InviteCode as InviteCodeDB
|
||||
from loveace.database.auth.register import RegisterCoolDown
|
||||
from loveace.database.auth.token import AuthMEToken
|
||||
from loveace.database.auth.user import ACEUser
|
||||
from loveace.database.creator import get_db_session
|
||||
from loveace.router.dependencies.logger import no_user_logger_mixin
|
||||
from loveace.router.endpoint.auth.model.register import (
|
||||
InviteCodeRequest,
|
||||
InviteCodeResponse,
|
||||
InviteErrorToCode,
|
||||
RegisterErrorToCode,
|
||||
RegisterRequest,
|
||||
RegisterResponse,
|
||||
)
|
||||
from loveace.router.schemas.uniresponse import UniResponseModel
|
||||
from loveace.service.remote.aufe import AUFEService
|
||||
from loveace.service.remote.aufe.depends import get_aufe_service
|
||||
from loveace.utils.rsa import RSAUtils
|
||||
|
||||
register_router = APIRouter(prefix="/register")
|
||||
|
||||
|
||||
temp_tokens = []
|
||||
|
||||
rsa_util = RSAUtils.get_or_create_rsa_utils()
|
||||
|
||||
|
||||
@register_router.post(
|
||||
"/invite",
|
||||
response_model=UniResponseModel[InviteCodeResponse],
|
||||
responses=InviteErrorToCode.gen_code_table(),
|
||||
summary="邀请码验证",
|
||||
)
|
||||
async def register(
|
||||
invite_code: InviteCodeRequest,
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
logger: LoggerMixin = Depends(no_user_logger_mixin),
|
||||
) -> UniResponseModel[InviteCodeResponse] | JSONResponse:
|
||||
"""
|
||||
验证邀请码并返回临时 Token
|
||||
|
||||
✅ 功能特性:
|
||||
- 验证邀请码的有效性
|
||||
- 生成临时 Token 用于后续注册步骤
|
||||
- 邀请码一次性使用
|
||||
|
||||
💡 使用场景:
|
||||
- 用户注册流程的第一步
|
||||
- 邀请制系统的验证
|
||||
|
||||
Args:
|
||||
invite_code: 邀请码请求对象
|
||||
db: 数据库会话
|
||||
logger: 日志记录器
|
||||
|
||||
Returns:
|
||||
InviteCodeResponse: 包含临时 Token
|
||||
"""
|
||||
try:
|
||||
async with db as session:
|
||||
logger.info(f"邀请码: {invite_code.invite_code}")
|
||||
invite = select(InviteCodeDB).where(
|
||||
InviteCodeDB.code == invite_code.invite_code
|
||||
)
|
||||
result = await session.execute(invite)
|
||||
invite_data = result.scalars().first()
|
||||
if invite_data is None:
|
||||
logger.info(f"邀请码不存在: {invite_code.invite_code}")
|
||||
return InviteErrorToCode().invalid_invite_code.to_json_response(
|
||||
logger.trace_id
|
||||
)
|
||||
token = secrets.token_urlsafe(128)
|
||||
temp_tokens.append(token)
|
||||
return UniResponseModel[InviteCodeResponse](
|
||||
success=True,
|
||||
data=InviteCodeResponse(token=token),
|
||||
message="邀请码验证成功",
|
||||
error=None,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error("邀请码验证失败:")
|
||||
logger.exception(e)
|
||||
return InviteErrorToCode().server_error.to_json_response(logger.trace_id)
|
||||
|
||||
|
||||
@register_router.post(
|
||||
"/next",
|
||||
response_model=UniResponseModel[RegisterResponse],
|
||||
responses=RegisterErrorToCode.gen_code_table(),
|
||||
summary="用户注册",
|
||||
)
|
||||
async def register_user(
|
||||
register_info: RegisterRequest,
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
logger: LoggerMixin = Depends(no_user_logger_mixin),
|
||||
aufe_service: AUFEService = Depends(get_aufe_service),
|
||||
) -> UniResponseModel[RegisterResponse] | JSONResponse:
|
||||
"""
|
||||
用户注册,验证身份并创建账户
|
||||
|
||||
✅ 功能特性:
|
||||
- 通过 AUFE 服务验证 EC 密码和登录密码
|
||||
- 验证身份信息的有效性
|
||||
- 生成 Authme Token 用于登录
|
||||
|
||||
⚠️ 限制条件:
|
||||
- EC 密码或登录密码错误会触发 5 分钟冷却时间
|
||||
- 用户 ID 不能重复
|
||||
- 必须提供有效的邀请 Token
|
||||
|
||||
💡 使用场景:
|
||||
- 新用户注册
|
||||
- 创建学号对应的账户
|
||||
|
||||
Args:
|
||||
register_info: 包含用户 ID、EC 密码、登录密码和邀请 Token 的注册信息
|
||||
db: 数据库会话
|
||||
logger: 日志记录器
|
||||
aufe_service: AUFE 远程认证服务
|
||||
|
||||
Returns:
|
||||
RegisterResponse: 包含 Authme Token
|
||||
"""
|
||||
try:
|
||||
async with db as session:
|
||||
# COOLDOWN检查
|
||||
query = select(RegisterCoolDown).where(
|
||||
RegisterCoolDown.userid == register_info.userid
|
||||
)
|
||||
result = await session.execute(query)
|
||||
cooldown = result.scalars().first()
|
||||
if cooldown:
|
||||
if cooldown.expire_date > datetime.now():
|
||||
logger.info(f"用户ID注册冷却中: {register_info.userid}")
|
||||
return RegisterErrorToCode().userid_exists.to_json_response(
|
||||
logger.trace_id
|
||||
)
|
||||
else:
|
||||
await session.delete(cooldown)
|
||||
await session.commit()
|
||||
if register_info.token not in temp_tokens:
|
||||
logger.info(f"无效的注册Token: {register_info.token}")
|
||||
return RegisterErrorToCode().invalid_token.to_json_response(
|
||||
logger.trace_id
|
||||
)
|
||||
query = select(ACEUser).where(ACEUser.userid == register_info.userid)
|
||||
result = await session.execute(query)
|
||||
user = result.scalars().first()
|
||||
if user is not None:
|
||||
logger.info(f"用户ID已存在: {register_info.userid}")
|
||||
return RegisterErrorToCode().userid_exists.to_json_response(
|
||||
logger.trace_id
|
||||
)
|
||||
# 尝试使用AUFE服务验证EC密码
|
||||
try:
|
||||
ec_password = rsa_util.decrypt(register_info.ec_password)
|
||||
password = rsa_util.decrypt(register_info.password)
|
||||
except Exception as e:
|
||||
logger.info(f"用户 {register_info.userid} 提供的密码解密失败: {e}")
|
||||
return RegisterErrorToCode().decrypt_error.to_json_response(
|
||||
logger.trace_id
|
||||
)
|
||||
conn = await aufe_service.get_or_create_connection(
|
||||
userid=register_info.userid,
|
||||
ec_password=ec_password,
|
||||
password=password,
|
||||
)
|
||||
ec_login_status = await conn.ec_login()
|
||||
if not ec_login_status.success:
|
||||
cooldown_entry = RegisterCoolDown(
|
||||
userid=register_info.userid,
|
||||
expire_date=datetime.now() + timedelta(minutes=5),
|
||||
)
|
||||
session.add(cooldown_entry)
|
||||
await session.commit()
|
||||
if ec_login_status.fail_invalid_credentials:
|
||||
logger.info(f"EC密码错误: {register_info.userid}")
|
||||
return RegisterErrorToCode().ec_password_error.to_json_response(
|
||||
logger.trace_id
|
||||
)
|
||||
else:
|
||||
logger.error(f"AUFE服务异常: {ec_login_status}")
|
||||
return RegisterErrorToCode().ec_server_error.to_json_response(
|
||||
logger.trace_id
|
||||
)
|
||||
|
||||
uaap_login_status = await conn.uaap_login()
|
||||
if not uaap_login_status.success:
|
||||
cooldown_entry = RegisterCoolDown(
|
||||
userid=register_info.userid,
|
||||
expire_date=datetime.now() + timedelta(minutes=5),
|
||||
)
|
||||
session.add(cooldown_entry)
|
||||
await session.commit()
|
||||
if uaap_login_status.fail_invalid_credentials:
|
||||
logger.info(f"登录密码错误: {register_info.userid}")
|
||||
return RegisterErrorToCode().uaap_password_error.to_json_response(
|
||||
logger.trace_id
|
||||
)
|
||||
else:
|
||||
logger.error(f"AUFE服务异常: {uaap_login_status}")
|
||||
return RegisterErrorToCode().ec_server_error.to_json_response(
|
||||
logger.trace_id
|
||||
)
|
||||
# 创建新用户
|
||||
new_user = ACEUser(
|
||||
userid=register_info.userid,
|
||||
ec_password=register_info.ec_password,
|
||||
password=register_info.password,
|
||||
)
|
||||
session.add(new_user)
|
||||
await session.commit()
|
||||
# 注册成功后删除临时Token
|
||||
temp_tokens.remove(register_info.token)
|
||||
# 生成Authme Token
|
||||
authme_token = secrets.token_urlsafe(128)
|
||||
new_token = AuthMEToken(
|
||||
user_id=new_user.userid, token=authme_token, device_id=uuid4().hex
|
||||
)
|
||||
session.add(new_token)
|
||||
await session.commit()
|
||||
return UniResponseModel[RegisterResponse](
|
||||
success=True,
|
||||
data=RegisterResponse(token=authme_token),
|
||||
message="注册成功",
|
||||
error=None,
|
||||
)
|
||||
except ValueError as ve:
|
||||
logger.error("用户注册失败: RSA解密错误")
|
||||
logger.exception(ve)
|
||||
return RegisterErrorToCode().server_error.to_json_response(
|
||||
logger.trace_id, "RSA解密错误,请检查授权密文"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error("用户注册失败:")
|
||||
logger.exception(e)
|
||||
return RegisterErrorToCode().server_error.to_json_response(logger.trace_id)
|
||||
Reference in New Issue
Block a user