93 lines
3.3 KiB
Python
93 lines
3.3 KiB
Python
|
#!/usr/bin/python
|
|||
|
# -*- coding: utf-8 -*-
|
|||
|
# @version : 1.0
|
|||
|
# @Create Time : 2022/11/9 10:15
|
|||
|
# @File : login.py
|
|||
|
# @IDE : PyCharm
|
|||
|
# @desc : 登录验证装饰器
|
|||
|
|
|||
|
from fastapi import Request
|
|||
|
from pydantic import BaseModel, field_validator
|
|||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|||
|
from application.settings import DEFAULT_AUTH_ERROR_MAX_NUMBER, DEMO, REDIS_DB_ENABLE
|
|||
|
from apps.vadmin.auth import crud, schemas
|
|||
|
from core.database import redis_getter
|
|||
|
from core.validator import vali_telephone
|
|||
|
from utils.count import Count
|
|||
|
|
|||
|
|
|||
|
class LoginForm(BaseModel):
|
|||
|
telephone: str
|
|||
|
password: str
|
|||
|
method: str = '0' # 认证方式,0:密码登录,1:短信登录,2:微信一键登录
|
|||
|
platform: str = '0' # 登录平台,0:PC端管理系统,1:移动端管理系统
|
|||
|
|
|||
|
# 重用验证器:https://docs.pydantic.dev/dev-v2/usage/validators/#reuse-validators
|
|||
|
normalize_telephone = field_validator('telephone')(vali_telephone)
|
|||
|
|
|||
|
|
|||
|
class WXLoginForm(BaseModel):
|
|||
|
telephone: str | None = None
|
|||
|
code: str
|
|||
|
method: str = '2' # 认证方式,0:密码登录,1:短信登录,2:微信一键登录
|
|||
|
platform: str = '1' # 登录平台,0:PC端管理系统,1:移动端管理系统
|
|||
|
|
|||
|
|
|||
|
class LoginResult(BaseModel):
|
|||
|
status: bool | None = False
|
|||
|
user: schemas.UserPasswordOut | None = None
|
|||
|
msg: str | None = None
|
|||
|
|
|||
|
class Config:
|
|||
|
arbitrary_types_allowed = True
|
|||
|
|
|||
|
|
|||
|
class LoginValidation:
|
|||
|
|
|||
|
"""
|
|||
|
验证用户登录时提交的数据是否有效
|
|||
|
"""
|
|||
|
|
|||
|
def __init__(self, func):
|
|||
|
self.func = func
|
|||
|
|
|||
|
async def __call__(self, data: LoginForm, db: AsyncSession, request: Request) -> LoginResult:
|
|||
|
self.result = LoginResult()
|
|||
|
if data.platform not in ["0", "1"] or data.method not in ["0", "1"]:
|
|||
|
self.result.msg = "无效参数"
|
|||
|
return self.result
|
|||
|
user = await crud.UserDal(db).get_data(telephone=data.telephone, v_return_none=True)
|
|||
|
if not user:
|
|||
|
self.result.msg = "该手机号不存在!"
|
|||
|
return self.result
|
|||
|
|
|||
|
result = await self.func(self, data=data, user=user, request=request)
|
|||
|
|
|||
|
if REDIS_DB_ENABLE:
|
|||
|
count_key = f"{data.telephone}_password_auth" if data.method == '0' else f"{data.telephone}_sms_auth"
|
|||
|
count = Count(redis_getter(request), count_key)
|
|||
|
else:
|
|||
|
count = None
|
|||
|
|
|||
|
if not result.status:
|
|||
|
self.result.msg = result.msg
|
|||
|
if not DEMO and count:
|
|||
|
number = await count.add(ex=86400)
|
|||
|
if number >= DEFAULT_AUTH_ERROR_MAX_NUMBER:
|
|||
|
await count.reset()
|
|||
|
# 如果等于最大次数,那么就将用户 is_active=False
|
|||
|
user.is_active = False
|
|||
|
await db.flush()
|
|||
|
elif not user.is_active:
|
|||
|
self.result.msg = "此手机号已被冻结!"
|
|||
|
elif data.platform in ["0", "1"] and not user.is_staff:
|
|||
|
self.result.msg = "此手机号无权限!"
|
|||
|
else:
|
|||
|
if not DEMO and count:
|
|||
|
await count.delete()
|
|||
|
self.result.msg = "OK"
|
|||
|
self.result.status = True
|
|||
|
self.result.user = schemas.UserPasswordOut.model_validate(user)
|
|||
|
await crud.UserDal(db).update_login_info(user, request.client.host)
|
|||
|
return self.result
|