|
|
@@ -22,9 +22,10 @@ from app.core.security import (
|
|
|
decode_access_token,
|
|
|
)
|
|
|
from app.utils.captcha_util import CaptchaUtil
|
|
|
-from app.utils.common_util import get_random_character
|
|
|
+from app.utils.common_util import get_random_character, generate_random_code
|
|
|
from app.utils.hash_bcrpy_util import PwdUtil
|
|
|
from app.utils.ip_local_util import IpLocalUtil
|
|
|
+from app.utils.sms_util import SendSmsRequest, SmsTemplateEnum, SmsSender
|
|
|
|
|
|
from .schema import (
|
|
|
AuthSchema,
|
|
|
@@ -35,7 +36,9 @@ from .schema import (
|
|
|
JWTPayloadSchema,
|
|
|
LoginMiniRequestSchema,
|
|
|
LogoutPayloadSchema,
|
|
|
- RefreshTokenPayloadSchema, SmsCodeSchema,
|
|
|
+ RefreshTokenPayloadSchema,
|
|
|
+ SmsCodeSchema,
|
|
|
+ SmsLoginRequestSchema,
|
|
|
)
|
|
|
|
|
|
CaptchaKey = NewType("CaptchaKey", str)
|
|
|
@@ -155,6 +158,62 @@ class LoginService:
|
|
|
|
|
|
return token
|
|
|
|
|
|
+ @classmethod
|
|
|
+ async def authenticate_sms_user_service(
|
|
|
+ cls,
|
|
|
+ request: Request,
|
|
|
+ redis: Redis,
|
|
|
+ login_form: SmsLoginRequestSchema,
|
|
|
+ db: AsyncSession,
|
|
|
+ ) -> JWTOutSchema:
|
|
|
+ """
|
|
|
+ 短信验证码登录
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ - request (Request): FastAPI请求对象
|
|
|
+ - redis (Redis): Redis客户端对象
|
|
|
+ - login_form (SmsLoginRequestSchema): 短信登录表单数据
|
|
|
+ - db (AsyncSession): 数据库会话对象
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ - JWTOutSchema: 包含访问令牌和刷新令牌的响应模型
|
|
|
+
|
|
|
+ 异常:
|
|
|
+ - CustomException: 认证失败时抛出异常。
|
|
|
+ """
|
|
|
+ mobile = login_form.mobile
|
|
|
+ code = login_form.code
|
|
|
+
|
|
|
+ # 验证验证码
|
|
|
+ verify_result = await SmsCodeService.verify_sms_code_service(login_form, redis)
|
|
|
+ if not verify_result:
|
|
|
+ raise CustomException(msg="验证码已过期或错误")
|
|
|
+
|
|
|
+ # 根据手机号查找用户
|
|
|
+ auth = AuthSchema(db=db)
|
|
|
+ user = await UserCRUD(auth).get_by_mobile_crud(mobile=mobile)
|
|
|
+
|
|
|
+ if not user:
|
|
|
+ raise CustomException(msg="用户不存在")
|
|
|
+
|
|
|
+ if user.status == "1":
|
|
|
+ raise CustomException(msg="用户已被停用")
|
|
|
+
|
|
|
+ # 更新最后登录时间
|
|
|
+ user = await UserCRUD(auth).update_last_login_crud(id=user.id)
|
|
|
+ if not user:
|
|
|
+ raise CustomException(msg="用户不存在")
|
|
|
+
|
|
|
+ # 创建token
|
|
|
+ token = await cls.create_token_service(
|
|
|
+ request=request,
|
|
|
+ redis=redis,
|
|
|
+ user=user,
|
|
|
+ login_type="sms",
|
|
|
+ )
|
|
|
+
|
|
|
+ return token
|
|
|
+
|
|
|
@classmethod
|
|
|
async def create_token_service(
|
|
|
cls, request: Request, redis: Redis, user: UserModel, login_type: str
|
|
|
@@ -610,12 +669,12 @@ class AutoLoginService:
|
|
|
class SmsCodeService:
|
|
|
"""短信验证码服务"""
|
|
|
SMS_CODE_PREFIX = "sms_code"
|
|
|
- SMS_CODE_EXPIRE = 60 * 5
|
|
|
+ SMS_CODE_EXPIRE = 60 * 10
|
|
|
|
|
|
@classmethod
|
|
|
async def send_sms_code_service(
|
|
|
cls, sms_code: SmsCodeSchema, redis: Redis
|
|
|
- ) -> None:
|
|
|
+ ) -> bool:
|
|
|
"""
|
|
|
发送短信验证码
|
|
|
|
|
|
@@ -626,7 +685,25 @@ class SmsCodeService:
|
|
|
异常:
|
|
|
- CustomException: 验证码发送失败时抛出异常。
|
|
|
"""
|
|
|
- pass
|
|
|
+ template = SmsTemplateEnum.get_template_by_name(sms_code.template_name)
|
|
|
+
|
|
|
+ code = generate_random_code(6)
|
|
|
+
|
|
|
+ redis_key = f"{cls.SMS_CODE_PREFIX}:{sms_code.template_name}:{sms_code.mobile}"
|
|
|
+ await RedisCURD(redis).set(
|
|
|
+ key=redis_key,
|
|
|
+ value=code,
|
|
|
+ expire=cls.SMS_CODE_EXPIRE,
|
|
|
+ )
|
|
|
+
|
|
|
+ request = SendSmsRequest(
|
|
|
+ phone_numbers=sms_code.mobile,
|
|
|
+ template_code=template.template_code,
|
|
|
+ template_param=template.template_param_fn(code=code),
|
|
|
+ )
|
|
|
+
|
|
|
+ return await SmsSender.send_sms(request)
|
|
|
+
|
|
|
|
|
|
@classmethod
|
|
|
async def verify_sms_code_service(
|
|
|
@@ -645,8 +722,9 @@ class SmsCodeService:
|
|
|
异常:
|
|
|
- CustomException: 验证码验证失败时抛出异常。
|
|
|
"""
|
|
|
- redis_key = f"{cls.SMS_CODE_PREFIX}:{sms_code.mobile}"
|
|
|
+ redis_key = f"{cls.SMS_CODE_PREFIX}:{sms_code.template_name}:{sms_code.mobile}"
|
|
|
code = await RedisCURD(redis).get(redis_key)
|
|
|
if not code or code != sms_code.code:
|
|
|
return False
|
|
|
+ await RedisCURD(redis).delete(redis_key)
|
|
|
return True
|