版本升级
1. 新增(kinit-api):防止密码破解,新增输入5次错误密码,账号锁定,使用过期时间为1天 2. 新增(kinit-api):新增 OpenAuth 开放认证方式 3. 新增(kinit-api,kinit-uni):创建用户时,如果没有头像,则添加默认头像链接 4. 优化(kinit-api):登录接口优化 5. 新增(kinit-api):新增Count计数类 6. 更新(kinit-admin):前端升级到vue-element-plus-admin最新版本1.9.4
This commit is contained in:
parent
7fbdcb3b0f
commit
ebcdac7796
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vue-element-plus-admin",
|
"name": "vue-element-plus-admin",
|
||||||
"version": "1.9.2",
|
"version": "1.9.4",
|
||||||
"description": "一套基于vue3、element-plus、typesScript、vite4的后台集成方案。",
|
"description": "一套基于vue3、element-plus、typesScript、vite4的后台集成方案。",
|
||||||
"author": "Archer <502431556@qq.com>",
|
"author": "Archer <502431556@qq.com>",
|
||||||
"private": false,
|
"private": false,
|
||||||
@ -25,28 +25,28 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||||
"@iconify/iconify": "^3.0.1",
|
"@iconify/iconify": "^3.1.0",
|
||||||
"@kjgl77/datav-vue3": "^1.4.2",
|
"@kjgl77/datav-vue3": "^1.4.2",
|
||||||
"@vueuse/core": "^9.10.0",
|
"@vueuse/core": "^9.13.0",
|
||||||
"@wangeditor/editor": "^5.1.23",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
"@wangeditor/editor-for-vue": "^5.1.10",
|
"@wangeditor/editor-for-vue": "^5.1.10",
|
||||||
"@zxcvbn-ts/core": "^2.1.0",
|
"@zxcvbn-ts/core": "^2.2.1",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"axios": "^1.2.2",
|
"axios": "^1.3.4",
|
||||||
"echarts": "^5.4.1",
|
"echarts": "^5.4.1",
|
||||||
"echarts-wordcloud": "^2.1.0",
|
"echarts-wordcloud": "^2.1.0",
|
||||||
"element-plus": "2.2.28",
|
"element-plus": "2.2.32",
|
||||||
"intro.js": "^6.0.0",
|
"intro.js": "^6.0.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mitt": "^3.0.0",
|
"mitt": "^3.0.0",
|
||||||
"mockjs": "^1.1.0",
|
"mockjs": "^1.1.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pinia": "2.0.29",
|
"pinia": "^2.0.32",
|
||||||
"qrcode": "^1.5.1",
|
"qrcode": "^1.5.1",
|
||||||
"qs": "^6.11.0",
|
"qs": "^6.11.0",
|
||||||
"url": "^0.11.0",
|
"url": "^0.11.0",
|
||||||
"vue": "3.2.45",
|
"vue": "3.2.47",
|
||||||
"vue-i18n": "9.2.2",
|
"vue-i18n": "9.2.2",
|
||||||
"vue-router": "^4.1.6",
|
"vue-router": "^4.1.6",
|
||||||
"vue-types": "^5.0.2",
|
"vue-types": "^5.0.2",
|
||||||
@ -55,48 +55,48 @@
|
|||||||
"web-storage-cache": "^1.1.1"
|
"web-storage-cache": "^1.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^17.4.2",
|
"@commitlint/cli": "^17.4.4",
|
||||||
"@commitlint/config-conventional": "^17.4.2",
|
"@commitlint/config-conventional": "^17.4.4",
|
||||||
"@iconify/json": "^2.2.7",
|
"@iconify/json": "^2.2.29",
|
||||||
"@intlify/unplugin-vue-i18n": "^0.8.1",
|
"@intlify/unplugin-vue-i18n": "^0.8.2",
|
||||||
"@purge-icons/generated": "^0.9.0",
|
"@purge-icons/generated": "^0.9.0",
|
||||||
"@types/intro.js": "^5.1.0",
|
"@types/intro.js": "^5.1.1",
|
||||||
"@types/lodash-es": "^4.17.6",
|
"@types/lodash-es": "^4.17.6",
|
||||||
"@types/node": "^18.11.18",
|
"@types/node": "^18.14.2",
|
||||||
"@types/nprogress": "^0.2.0",
|
"@types/nprogress": "^0.2.0",
|
||||||
"@types/qrcode": "^1.5.0",
|
"@types/qrcode": "^1.5.0",
|
||||||
"@types/qs": "^6.9.7",
|
"@types/qs": "^6.9.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.48.1",
|
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
||||||
"@typescript-eslint/parser": "^5.48.1",
|
"@typescript-eslint/parser": "^5.54.0",
|
||||||
"@vitejs/plugin-legacy": "^3.0.1",
|
"@vitejs/plugin-legacy": "^4.0.1",
|
||||||
"@vitejs/plugin-vue": "^4.0.0",
|
"@vitejs/plugin-vue": "^4.0.0",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"consola": "^2.15.3",
|
"consola": "^2.15.3",
|
||||||
"eslint": "^8.32.0",
|
"eslint": "^8.35.0",
|
||||||
"eslint-config-prettier": "^8.6.0",
|
"eslint-config-prettier": "^8.6.0",
|
||||||
"eslint-define-config": "^1.14.0",
|
"eslint-define-config": "^1.15.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"eslint-plugin-vue": "^9.9.0",
|
"eslint-plugin-vue": "^9.9.0",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"less": "^4.1.3",
|
"less": "^4.1.3",
|
||||||
"lint-staged": "^13.1.0",
|
"lint-staged": "^13.1.2",
|
||||||
"plop": "^3.1.1",
|
"plop": "^3.1.2",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
"postcss-html": "^1.5.0",
|
"postcss-html": "^1.5.0",
|
||||||
"postcss-less": "^6.0.0",
|
"postcss-less": "^6.0.0",
|
||||||
"prettier": "^2.8.3",
|
"prettier": "^2.8.4",
|
||||||
"rimraf": "^4.0.7",
|
"rimraf": "^4.1.2",
|
||||||
"rollup": "^3.10.0",
|
"rollup": "^3.17.3",
|
||||||
"stylelint": "^14.16.1",
|
"stylelint": "^15.2.0",
|
||||||
"stylelint-config-html": "^1.1.0",
|
"stylelint-config-html": "^1.1.0",
|
||||||
"stylelint-config-prettier": "^9.0.4",
|
"stylelint-config-prettier": "^9.0.5",
|
||||||
"stylelint-config-recommended": "^9.0.0",
|
"stylelint-config-recommended": "^10.0.1",
|
||||||
"stylelint-config-standard": "^29.0.0",
|
"stylelint-config-standard": "^30.0.1",
|
||||||
"stylelint-order": "^6.0.1",
|
"stylelint-order": "^6.0.2",
|
||||||
"terser": "^5.16.1",
|
"terser": "^5.16.5",
|
||||||
"typescript": "4.9.4",
|
"typescript": "4.9.5",
|
||||||
"vite": "4.0.4",
|
"vite": "4.1.4",
|
||||||
"vite-plugin-ejs": "^1.6.4",
|
"vite-plugin-ejs": "^1.6.4",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-plugin-mock": "^2.9.6",
|
"vite-plugin-mock": "^2.9.6",
|
||||||
@ -105,7 +105,7 @@
|
|||||||
"vite-plugin-style-import": "2.0.0",
|
"vite-plugin-style-import": "2.0.0",
|
||||||
"vite-plugin-svg-icons": "^2.0.1",
|
"vite-plugin-svg-icons": "^2.0.1",
|
||||||
"vite-plugin-windicss": "^1.8.10",
|
"vite-plugin-windicss": "^1.8.10",
|
||||||
"vue-tsc": "^1.0.24",
|
"vue-tsc": "^1.2.0",
|
||||||
"windicss": "^3.5.6",
|
"windicss": "^3.5.6",
|
||||||
"windicss-analysis": "^0.3.5"
|
"windicss-analysis": "^0.3.5"
|
||||||
},
|
},
|
||||||
|
@ -107,7 +107,13 @@ const toggleClick = () => {
|
|||||||
v-bind="getBindItemValue(item)"
|
v-bind="getBindItemValue(item)"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<slot :name="`${item.field}-label`" :label="item.label">{{ item.label }}</slot>
|
<slot
|
||||||
|
:name="`${item.field}-label`"
|
||||||
|
:row="{
|
||||||
|
label: item.label
|
||||||
|
}"
|
||||||
|
>{{ item.label }}</slot
|
||||||
|
>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #default>
|
<template #default>
|
||||||
|
@ -412,7 +412,10 @@ watch(
|
|||||||
{
|
{
|
||||||
icon: 'ant-design:close-outlined',
|
icon: 'ant-design:close-outlined',
|
||||||
label: t('common.closeTab'),
|
label: t('common.closeTab'),
|
||||||
disabled: !!visitedViews?.length && selectedTag?.meta.affix
|
disabled: !!visitedViews?.length && selectedTag?.meta.affix,
|
||||||
|
command: () => {
|
||||||
|
closeSelectedTag(selectedTag!)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
divided: true,
|
divided: true,
|
||||||
|
@ -39,3 +39,9 @@ const themeChange = (val: boolean) => {
|
|||||||
@change="themeChange"
|
@change="themeChange"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
:deep(.el-switch__core .el-switch__inner .is-icon) {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -214,7 +214,6 @@ const filterFormSchema = (crudSchema: CrudSchema[], allSchemas: AllSchemas): For
|
|||||||
for (const task of formRequestTask) {
|
for (const task of formRequestTask) {
|
||||||
task()
|
task()
|
||||||
}
|
}
|
||||||
console.log(formSchema)
|
|
||||||
return formSchema
|
return formSchema
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,7 +242,7 @@ const filterDescriptionsSchema = (crudSchema: CrudSchema[]): DescriptionsSchema[
|
|||||||
|
|
||||||
// 给options添加国际化
|
// 给options添加国际化
|
||||||
const filterOptions = (options: Recordable, labelField?: string) => {
|
const filterOptions = (options: Recordable, labelField?: string) => {
|
||||||
return options.map((v: Recordable) => {
|
return options?.map((v: Recordable) => {
|
||||||
if (labelField) {
|
if (labelField) {
|
||||||
v['labelField'] = t(v.labelField)
|
v['labelField'] = t(v.labelField)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,2 +1,8 @@
|
|||||||
@import './var.css';
|
@import './var.css';
|
||||||
@import 'element-plus/theme-chalk/dark/css-vars.css';
|
@import 'element-plus/theme-chalk/dark/css-vars.css';
|
||||||
|
|
||||||
|
// 解决抽屉弹出时,body宽度变化的问题
|
||||||
|
.el-popup-parent--hidden {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ from fastapi.security import OAuth2PasswordBearer
|
|||||||
"""
|
"""
|
||||||
系统版本
|
系统版本
|
||||||
"""
|
"""
|
||||||
VERSION = "1.6.0"
|
VERSION = "1.6.1"
|
||||||
|
|
||||||
"""安全警告: 不要在生产中打开调试运行!"""
|
"""安全警告: 不要在生产中打开调试运行!"""
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
@ -105,6 +105,10 @@ EVENTS = [
|
|||||||
"""
|
"""
|
||||||
# 默认密码,"0" 默认为手机号后六位
|
# 默认密码,"0" 默认为手机号后六位
|
||||||
DEFAULT_PASSWORD = "0"
|
DEFAULT_PASSWORD = "0"
|
||||||
|
# 默认头像
|
||||||
|
DEFAULT_AVATAR = "https://vv-reserve.oss-cn-hangzhou.aliyuncs.com/avatar/2023-01-27/1674820804e81e7631.png"
|
||||||
|
# 默认登陆时最大输入密码或验证码错误次数
|
||||||
|
DEFAULT_AUTH_ERROR_MAX_NUMBER = 5
|
||||||
# 是否开启保存登录日志
|
# 是否开启保存登录日志
|
||||||
LOGIN_LOG_RECORD = not DEBUG
|
LOGIN_LOG_RECORD = not DEBUG
|
||||||
# 是否开启保存每次请求日志到本地
|
# 是否开启保存每次请求日志到本地
|
||||||
|
@ -60,6 +60,7 @@ class UserDal(DalBase):
|
|||||||
raise CustomException("手机号已存在!", code=status.HTTP_ERROR)
|
raise CustomException("手机号已存在!", code=status.HTTP_ERROR)
|
||||||
password = data.telephone[5:12] if settings.DEFAULT_PASSWORD == "0" else settings.DEFAULT_PASSWORD
|
password = data.telephone[5:12] if settings.DEFAULT_PASSWORD == "0" else settings.DEFAULT_PASSWORD
|
||||||
data.password = self.model.get_password_hash(password)
|
data.password = self.model.get_password_hash(password)
|
||||||
|
data.avatar = data.avatar if data.avatar else settings.DEFAULT_AVATAR
|
||||||
obj = self.model(**data.dict(exclude={'role_ids'}))
|
obj = self.model(**data.dict(exclude={'role_ids'}))
|
||||||
roles = await RoleDal(self.db).get_datas(limit=0, id=("in", data.role_ids), v_return_objs=True)
|
roles = await RoleDal(self.db).get_datas(limit=0, id=("in", data.role_ids), v_return_objs=True)
|
||||||
for role in roles:
|
for role in roles:
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
# @desc : 获取认证后的信息工具
|
# @desc : 获取认证后的信息工具
|
||||||
|
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from jose import jwt, JWTError
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy.orm import joinedload
|
from sqlalchemy.orm import joinedload
|
||||||
from apps.vadmin.auth.crud import UserDal
|
from apps.vadmin.auth.crud import UserDal
|
||||||
@ -33,6 +35,61 @@ def get_user_permissions(user: VadminUser) -> set:
|
|||||||
return permissions
|
return permissions
|
||||||
|
|
||||||
|
|
||||||
|
class OpenAuth(AuthValidation):
|
||||||
|
|
||||||
|
"""
|
||||||
|
开放认证,无认证也可以访问
|
||||||
|
认证了以后可以获取到用户信息,无认证则获取不到
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate_token(cls, token: str | None, db: AsyncSession) -> str | None:
|
||||||
|
"""
|
||||||
|
验证用户 token,没有则返回 None
|
||||||
|
"""
|
||||||
|
if not token:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
|
||||||
|
telephone: str = payload.get("sub")
|
||||||
|
if telephone is None:
|
||||||
|
return None
|
||||||
|
except JWTError:
|
||||||
|
return None
|
||||||
|
return telephone
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def validate_user(cls, request: Request, user: VadminUser, db: AsyncSession) -> Auth:
|
||||||
|
"""
|
||||||
|
验证用户信息
|
||||||
|
"""
|
||||||
|
if user is None:
|
||||||
|
return Auth(db=db)
|
||||||
|
elif not user.is_active:
|
||||||
|
return Auth(db=db)
|
||||||
|
request.scope["telephone"] = user.telephone
|
||||||
|
try:
|
||||||
|
request.scope["body"] = await request.body()
|
||||||
|
except RuntimeError:
|
||||||
|
request.scope["body"] = "获取失败"
|
||||||
|
return Auth(user=user, db=db)
|
||||||
|
|
||||||
|
async def __call__(
|
||||||
|
self,
|
||||||
|
request: Request,
|
||||||
|
token: str = Depends(settings.oauth2_scheme),
|
||||||
|
db: AsyncSession = Depends(db_getter)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
每次调用依赖此类的接口会执行该方法
|
||||||
|
"""
|
||||||
|
telephone = self.validate_token(token, db)
|
||||||
|
if telephone:
|
||||||
|
user = await UserDal(db).get_data(telephone=telephone, v_return_none=True)
|
||||||
|
return await self.validate_user(request, user, db)
|
||||||
|
return Auth(db=db)
|
||||||
|
|
||||||
|
|
||||||
class AllUserAuth(AuthValidation):
|
class AllUserAuth(AuthValidation):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -39,77 +39,66 @@ from core.data_types import Telephone
|
|||||||
app = APIRouter()
|
app = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@app.post("/login/", summary="手机号密码登录")
|
@app.post("/login/", summary="手机号密码登录", description="员工登录通道,限制最多输错次数,达到最大值后将is_active=False")
|
||||||
async def login_for_access_token(
|
async def login_for_access_token(
|
||||||
request: Request,
|
request: Request,
|
||||||
data: LoginForm,
|
data: LoginForm,
|
||||||
manage: LoginManage = Depends(),
|
manage: LoginManage = Depends(),
|
||||||
db: AsyncSession = Depends(db_getter)
|
db: AsyncSession = Depends(db_getter)
|
||||||
):
|
):
|
||||||
if data.method == "0":
|
try:
|
||||||
result = await manage.password_login(data, db, request)
|
if data.method == "0":
|
||||||
elif data.method == "1":
|
result = await manage.password_login(data, db, request)
|
||||||
result = await manage.sms_login(data, db, request)
|
elif data.method == "1":
|
||||||
else:
|
result = await manage.sms_login(data, db, request)
|
||||||
return ErrorResponse(msg="请使用正确的登录方式")
|
else:
|
||||||
if not result.status:
|
raise ValueError("无效参数")
|
||||||
resp = {"message": result.msg}
|
|
||||||
await VadminLoginRecord.create_login_record(db, data, result.status, request, resp)
|
|
||||||
return ErrorResponse(msg=result.msg)
|
|
||||||
user = result.user
|
|
||||||
|
|
||||||
if data.platform in ["0", "1"] and not user.is_staff:
|
if not result.status:
|
||||||
msg = "此手机号无登录权限"
|
raise ValueError(result.msg)
|
||||||
await VadminLoginRecord.create_login_record(db, data, result.status, request, {"message": msg})
|
|
||||||
return ErrorResponse(msg=msg)
|
|
||||||
|
|
||||||
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||||
access_token = LoginManage.create_access_token(data={"sub": user.telephone}, expires_delta=access_token_expires)
|
token = LoginManage.create_access_token(data={"sub": result.user.telephone}, expires_delta=token_expires)
|
||||||
resp = {
|
resp = {
|
||||||
"access_token": access_token,
|
"access_token": token,
|
||||||
"token_type": "bearer",
|
"token_type": "bearer",
|
||||||
"is_reset_password": user.is_reset_password,
|
"is_reset_password": result.user.is_reset_password,
|
||||||
"is_wx_server_openid": user.is_wx_server_openid
|
"is_wx_server_openid": result.user.is_wx_server_openid
|
||||||
}
|
}
|
||||||
await VadminLoginRecord.create_login_record(db, data, result.status, request, resp)
|
await VadminLoginRecord.create_login_record(db, data, True, request, resp)
|
||||||
return SuccessResponse(resp)
|
return SuccessResponse(resp)
|
||||||
|
except ValueError as e:
|
||||||
|
await VadminLoginRecord.create_login_record(db, data, False, request, {"message": str(e)})
|
||||||
|
return ErrorResponse(msg=str(e))
|
||||||
|
|
||||||
|
|
||||||
@app.post("/wx/login/", summary="微信服务端一键登录")
|
@app.post("/wx/login/", summary="微信服务端一键登录", description="员工登录通道")
|
||||||
async def wx_login_for_access_token(request: Request, data: WXLoginForm, db: AsyncSession = Depends(db_getter)):
|
async def wx_login_for_access_token(request: Request, data: WXLoginForm, db: AsyncSession = Depends(db_getter)):
|
||||||
if data.platform not in ["0", "1"]:
|
try:
|
||||||
msg = "错误平台"
|
if data.platform != "1" or data.method != "2":
|
||||||
await VadminLoginRecord.create_login_record(db, data, False, request, {"message": msg})
|
raise ValueError("无效参数")
|
||||||
return ErrorResponse(msg=msg)
|
wx = WXOAuth(request.app.state.redis, 0)
|
||||||
wx = WXOAuth(request.app.state.redis, 0)
|
telephone = await wx.parsing_phone_number(data.code)
|
||||||
telephone = await wx.parsing_phone_number(data.code)
|
if not telephone:
|
||||||
if not telephone:
|
raise ValueError("无效Code")
|
||||||
msg = "无效Code"
|
data.telephone = telephone
|
||||||
await VadminLoginRecord.create_login_record(db, data, False, request, {"message": msg})
|
user = await UserDal(db).get_data(telephone=telephone, v_return_none=True)
|
||||||
return ErrorResponse(msg=msg)
|
if not user:
|
||||||
data.telephone = telephone
|
raise ValueError("此手机号不存在")
|
||||||
user = await UserDal(db).get_data(telephone=telephone, v_return_none=True)
|
elif not user.is_active:
|
||||||
msg = None
|
raise ValueError("此手机号已被冻结")
|
||||||
if not user:
|
except ValueError as e:
|
||||||
# 手机号不存在,创建新用户
|
await VadminLoginRecord.create_login_record(db, data, False, request, {"message": str(e)})
|
||||||
# model = UserIn(name=generate_string(), telephone=Telephone(telephone))
|
return ErrorResponse(msg=str(e))
|
||||||
# user = await UserDal(db).create_data(model, v_return_obj=True)
|
|
||||||
msg = "手机号不存在!"
|
|
||||||
elif not user.is_active:
|
|
||||||
msg = "此手机号已被冻结!"
|
|
||||||
elif data.platform in ["0", "1"] and not user.is_staff:
|
|
||||||
msg = "此手机号无登录权限"
|
|
||||||
if msg:
|
|
||||||
await VadminLoginRecord.create_login_record(db, data, False, request, {"message": msg})
|
|
||||||
return ErrorResponse(msg=msg)
|
|
||||||
# 更新登录时间
|
# 更新登录时间
|
||||||
await user.update_login_info(db, request.client.host)
|
await user.update_login_info(db, request.client.host)
|
||||||
|
|
||||||
# 登录成功创建 token
|
# 登录成功创建 token
|
||||||
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||||
access_token = LoginManage.create_access_token(data={"sub": user.telephone}, expires_delta=access_token_expires)
|
token = LoginManage.create_access_token(data={"sub": user.telephone}, expires_delta=token_expires)
|
||||||
resp = {
|
resp = {
|
||||||
"access_token": access_token,
|
"access_token": token,
|
||||||
"token_type": "bearer",
|
"token_type": "bearer",
|
||||||
"is_reset_password": user.is_reset_password,
|
"is_reset_password": user.is_reset_password,
|
||||||
"is_wx_server_openid": user.is_wx_server_openid
|
"is_wx_server_openid": user.is_wx_server_openid
|
||||||
|
@ -9,10 +9,13 @@
|
|||||||
from fastapi import Request, Depends
|
from fastapi import Request, Depends
|
||||||
from pydantic import BaseModel, validator
|
from pydantic import BaseModel, validator
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from application.settings import DEFAULT_AUTH_ERROR_MAX_NUMBER
|
||||||
from apps.vadmin.auth import models, crud, schemas
|
from apps.vadmin.auth import models, crud, schemas
|
||||||
from core.database import db_getter
|
from core.database import db_getter
|
||||||
from core.validator import vali_telephone
|
from core.validator import vali_telephone
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
from utils.count import Count
|
||||||
|
|
||||||
|
|
||||||
class LoginForm(BaseModel):
|
class LoginForm(BaseModel):
|
||||||
@ -52,8 +55,8 @@ class LoginValidation:
|
|||||||
|
|
||||||
async def __call__(self, data: LoginForm, db: AsyncSession, request: Request) -> LoginResult:
|
async def __call__(self, data: LoginForm, db: AsyncSession, request: Request) -> LoginResult:
|
||||||
self.result = LoginResult()
|
self.result = LoginResult()
|
||||||
if data.platform not in ["0", "1"]:
|
if data.platform not in ["0", "1"] or data.method not in ["0", "1"]:
|
||||||
self.result.msg = "错误平台"
|
self.result.msg = "无效参数"
|
||||||
return self.result
|
return self.result
|
||||||
user = await crud.UserDal(db).get_data(telephone=data.telephone, v_return_none=True)
|
user = await crud.UserDal(db).get_data(telephone=data.telephone, v_return_none=True)
|
||||||
if not user:
|
if not user:
|
||||||
@ -62,11 +65,23 @@ class LoginValidation:
|
|||||||
|
|
||||||
result = await self.func(self, data=data, user=user, request=request)
|
result = await self.func(self, data=data, user=user, request=request)
|
||||||
|
|
||||||
|
count_key = f"{data.telephone}_password_auth" if data.method == '0' else f"{data.telephone}_sms_auth"
|
||||||
|
count = Count(request.app.state.redis, count_key)
|
||||||
|
|
||||||
if not result.status:
|
if not result.status:
|
||||||
self.result.msg = result.msg
|
self.result.msg = result.msg
|
||||||
|
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:
|
elif not user.is_active:
|
||||||
self.result.msg = "此手机号已被冻结!"
|
self.result.msg = "此手机号已被冻结!"
|
||||||
elif user:
|
elif data.platform in ["0", "1"] and not user.is_staff:
|
||||||
|
self.result.msg = "此手机号无权限!"
|
||||||
|
else:
|
||||||
|
await count.delete()
|
||||||
self.result.msg = "OK"
|
self.result.msg = "OK"
|
||||||
self.result.status = True
|
self.result.status = True
|
||||||
self.result.user = schemas.UserSimpleOut.from_orm(user)
|
self.result.user = schemas.UserSimpleOut.from_orm(user)
|
||||||
|
@ -107,7 +107,7 @@ const actions = {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
getInfo().then(res => {
|
getInfo().then(res => {
|
||||||
const user = res.data
|
const user = res.data
|
||||||
const avatar = (user == null || user.avatar == "" || user.avatar == null) ? require("@/static/images/avatar.jpg") : user.avatar
|
const avatar = (user == null || user.avatar == "" || user.avatar == null) ? "https://vv-reserve.oss-cn-hangzhou.aliyuncs.com/avatar/2023-01-27/1674820804e81e7631.png" : user.avatar
|
||||||
const name = (user == null || user.name == "" || user.name == null) ? "" : user.name
|
const name = (user == null || user.name == "" || user.name == null) ? "" : user.name
|
||||||
commit('SET_ROLES', user.roles.map((item) => item.name) || ['ROLE_DEFAULT'])
|
commit('SET_ROLES', user.roles.map((item) => item.name) || ['ROLE_DEFAULT'])
|
||||||
commit('SET_PERMISSIONS', user.permissions)
|
commit('SET_PERMISSIONS', user.permissions)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user