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
|