diff --git a/kinit-admin/src/config/axios/service.ts b/kinit-admin/src/config/axios/service.ts index 5a03118..c4ac5ac 100644 --- a/kinit-admin/src/config/axios/service.ts +++ b/kinit-admin/src/config/axios/service.ts @@ -66,13 +66,13 @@ service.interceptors.response.use( // 这个状态码是和后端约定好的 const code = response.data.code || unauthorized_code const message = response.data.message || '后端接口无返回内容' - const refresh = response.data.refresh || false + const refresh = response.headers.refresh if (response.config.responseType === 'blob') { // 如果是文件流,直接过 return response } else if (code === result_code) { - if (refresh) { + if (refresh === '1') { // 因token快过期,刷新token refreshToken().then((res) => { const appStore = useAppStore() diff --git a/kinit-api/README.md b/kinit-api/README.md index f6b4168..bf6d647 100644 --- a/kinit-api/README.md +++ b/kinit-api/README.md @@ -43,7 +43,7 @@ Typer 官方文档:https://typer.tiangolo.com/ 开发语言:Python 3.10 -开发框架:Fastapi 0.94.1 +开发框架:Fastapi 0.95.0 ## 开发工具 diff --git a/kinit-api/application/settings.py b/kinit-api/application/settings.py index c3a5a2a..1aa3913 100644 --- a/kinit-api/application/settings.py +++ b/kinit-api/application/settings.py @@ -11,7 +11,7 @@ from fastapi.security import OAuth2PasswordBearer """ 系统版本 """ -VERSION = "1.6.4" +VERSION = "1.7.0" """安全警告: 不要在生产中打开调试运行!""" DEBUG = True @@ -132,4 +132,5 @@ MIDDLEWARES = [ "core.middleware.register_request_log_middleware" if REQUEST_LOG_RECORD else None, "core.middleware.register_operation_record_middleware" if OPERATION_LOG_RECORD and MONGO_DB_ENABLE else None, "core.middleware.register_demo_env_middleware" if DEMO else None, + "core.middleware.register_jwt_refresh_middleware" ] diff --git a/kinit-api/apps/vadmin/analysis/views.py b/kinit-api/apps/vadmin/analysis/views.py index 3960ccf..ea46918 100644 --- a/kinit-api/apps/vadmin/analysis/views.py +++ b/kinit-api/apps/vadmin/analysis/views.py @@ -30,7 +30,7 @@ async def get_banners(auth: Auth = Depends(AllUserAuth())): "id": 3, "image": "https://ktianc.oss-cn-beijing.aliyuncs.com/kinit/system/banner/2022-11-09/banner3.png" }, ] - return SuccessResponse(data, refresh=auth.refresh) + return SuccessResponse(data) @app.get("/user/access/source/", summary="用户来源") @@ -42,7 +42,7 @@ async def get_user_access_source(auth: Auth = Depends(AllUserAuth())): {"value": 135, "name": 'analysis.videoAdvertising'}, {"value": 1548, "name": 'analysis.searchEngines'} ] - return SuccessResponse(data, refresh=auth.refresh) + return SuccessResponse(data) @app.get("/weekly/user/activity/", summary="每周用户活跃量") @@ -56,7 +56,7 @@ async def get_weekly_user_activity(auth: Auth = Depends(AllUserAuth())): {"value": 1322, "name": 'analysis.saturday'}, {"value": 1324, "name": 'analysis.sunday'} ] - return SuccessResponse(data, refresh=auth.refresh) + return SuccessResponse(data) @app.get("/monthly/sales/", summary="每月销售额") @@ -75,4 +75,4 @@ async def get_monthly_sales(auth: Auth = Depends(AllUserAuth())): {"estimate": 118, "actual": 99, "name": 'analysis.november'}, {"estimate": 123, "actual": 123, "name": 'analysis.december'} ] - return SuccessResponse(data, refresh=auth.refresh) + return SuccessResponse(data) diff --git a/kinit-api/apps/vadmin/auth/utils/current.py b/kinit-api/apps/vadmin/auth/utils/current.py index 7a098fc..8c11a56 100644 --- a/kinit-api/apps/vadmin/auth/utils/current.py +++ b/kinit-api/apps/vadmin/auth/utils/current.py @@ -4,9 +4,8 @@ # @File : current.py # @IDE : PyCharm # @desc : 获取认证后的信息工具 -from datetime import datetime, timedelta + from typing import List, Optional -import jwt from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import joinedload from apps.vadmin.auth.crud import UserDal @@ -20,20 +19,6 @@ from core.database import db_getter from .validation.auth import Auth -def get_user_permissions(user: VadminUser) -> set: - """ - 获取员工用户所有权限列表 - """ - if any([role.is_admin for role in user.roles]): - return {'*.*.*'} - permissions = set() - for role_obj in user.roles: - for menu in role_obj.menus: - if menu.perms and not menu.disabled: - permissions.add(menu.perms) - return permissions - - class OpenAuth(AuthValidation): """ @@ -41,50 +26,6 @@ class OpenAuth(AuthValidation): 认证了以后可以获取到用户信息,无认证则获取不到 """ - @classmethod - def validate_token(cls, request: Request, token: str | None) -> 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") - exp: int = payload.get("exp") - is_refresh: bool = payload.get("is_refresh") - if telephone is None or is_refresh: - return None - # 计算当前时间 + 缓冲时间是否大于等于 JWT 过期时间 - buffer_time = (datetime.now() + timedelta(minutes=settings.ACCESS_TOKEN_CACHE_MINUTES)).timestamp() - if buffer_time >= exp: - request.scope["refresh"] = True - else: - request.scope["refresh"] = False - except jwt.exceptions.InvalidSignatureError: - return None - except jwt.exceptions.ExpiredSignatureError: - 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["user_id"] = user.id - request.scope["user_name"] = user.name - 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, @@ -94,11 +35,14 @@ class OpenAuth(AuthValidation): """ 每次调用依赖此类的接口会执行该方法 """ - telephone = self.validate_token(request, token) - if telephone: + if not settings.OAUTH_ENABLE: + return Auth(db=db) + try: + telephone = self.validate_token(request, token) user = await UserDal(db).get_data(telephone=telephone, v_return_none=True) return await self.validate_user(request, user, db) - return Auth(db=db) + except CustomException: + return Auth(db=db) class AllUserAuth(AuthValidation): @@ -120,8 +64,6 @@ class AllUserAuth(AuthValidation): if not settings.OAUTH_ENABLE: return Auth(db=db) telephone = self.validate_token(request, token) - if isinstance(telephone, Auth): - return telephone user = await UserDal(db).get_data(telephone=telephone, v_return_none=True) return await self.validate_user(request, user, db) @@ -152,12 +94,10 @@ class FullAdminAuth(AuthValidation): if not settings.OAUTH_ENABLE: return Auth(db=db) telephone = self.validate_token(request, token) - if isinstance(telephone, Auth): - return telephone options = [joinedload(VadminUser.roles), joinedload("roles.menus")] user = await UserDal(db).get_data(telephone=telephone, v_return_none=True, v_options=options, is_staff=True) result = await self.validate_user(request, user, db) - permissions = get_user_permissions(user) + permissions = self.get_user_permissions(user) if permissions != {'*.*.*'} and self.permissions: if not (self.permissions & permissions): raise CustomException(msg="无权限操作", code=status.HTTP_403_FORBIDDEN) diff --git a/kinit-api/apps/vadmin/auth/utils/validation/auth.py b/kinit-api/apps/vadmin/auth/utils/validation/auth.py index 36ee088..369ef9d 100644 --- a/kinit-api/apps/vadmin/auth/utils/validation/auth.py +++ b/kinit-api/apps/vadmin/auth/utils/validation/auth.py @@ -4,20 +4,20 @@ # @File : auth.py # @IDE : PyCharm # @desc : 用户凭证验证装饰器 -from datetime import datetime, timedelta + from fastapi import Request import jwt from pydantic import BaseModel from application import settings from sqlalchemy.ext.asyncio import AsyncSession -from apps.vadmin.auth import models +from apps.vadmin.auth.models import VadminUser from core.exception import CustomException from utils import status +from datetime import timedelta, datetime class Auth(BaseModel): - user: models.VadminUser = None - refresh: bool = False + user: VadminUser = None db: AsyncSession class Config: @@ -33,7 +33,7 @@ class AuthValidation: error_code = status.HTTP_401_UNAUTHORIZED @classmethod - def validate_token(cls, request: Request, token: str) -> str: + def validate_token(cls, request: Request, token: str | None) -> str: """ 验证用户 token """ @@ -52,9 +52,9 @@ class AuthValidation: # print("当前时间", buffer_time, datetime.fromtimestamp(buffer_time)) # print("剩余时间", exp - buffer_time) if buffer_time >= exp: - request.scope["refresh"] = True + request.scope["refresh"] = 1 else: - request.scope["refresh"] = False + request.scope["refresh"] = 0 except jwt.exceptions.InvalidSignatureError: raise CustomException(msg="无效认证,请您重新登录", code=cls.error_code) except jwt.exceptions.ExpiredSignatureError: @@ -62,7 +62,7 @@ class AuthValidation: return telephone @classmethod - async def validate_user(cls, request: Request, user: models.VadminUser, db: AsyncSession) -> Auth: + async def validate_user(cls, request: Request, user: VadminUser, db: AsyncSession) -> Auth: """ 验证用户信息 """ @@ -70,12 +70,25 @@ class AuthValidation: raise CustomException(msg="未认证,请您重新登陆", code=cls.error_code, status_code=cls.error_code) elif not user.is_active: raise CustomException(msg="用户已被冻结!", code=cls.error_code, status_code=cls.error_code) + request.scope["telephone"] = user.telephone request.scope["user_id"] = user.id request.scope["user_name"] = user.name - request.scope["telephone"] = user.telephone try: request.scope["body"] = await request.body() except RuntimeError: request.scope["body"] = "获取失败" - refresh = request.scope.get("refresh", False) - return Auth(user=user, db=db, refresh=refresh) + return Auth(user=user, db=db) + + @classmethod + def get_user_permissions(cls, user: VadminUser) -> set: + """ + 获取员工用户所有权限列表 + """ + if any([role.is_admin for role in user.roles]): + return {'*.*.*'} + permissions = set() + for role_obj in user.roles: + for menu in role_obj.menus: + if menu.perms and not menu.disabled: + permissions.add(menu.perms) + return permissions diff --git a/kinit-api/apps/vadmin/auth/views.py b/kinit-api/apps/vadmin/auth/views.py index 1ec4058..ee4065f 100644 --- a/kinit-api/apps/vadmin/auth/views.py +++ b/kinit-api/apps/vadmin/auth/views.py @@ -11,7 +11,7 @@ from sqlalchemy.orm import joinedload from utils.response import SuccessResponse, ErrorResponse from . import schemas, crud, models from core.dependencies import IdList -from apps.vadmin.auth.utils.current import AllUserAuth, get_user_permissions, FullAdminAuth +from apps.vadmin.auth.utils.current import AllUserAuth, FullAdminAuth from apps.vadmin.auth.utils.validation.auth import Auth from .params import UserParams, RoleParams @@ -31,12 +31,12 @@ async def get_users( schema = schemas.UserOut datas = await crud.UserDal(auth.db).get_datas(**params.dict(), v_options=options, v_schema=schema) count = await crud.UserDal(auth.db).get_count(**params.to_count()) - return SuccessResponse(datas, count=count, refresh=auth.refresh) + return SuccessResponse(datas, count=count) @app.post("/users/", summary="创建用户") async def create_user(data: schemas.UserIn, auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.create"]))): - return SuccessResponse(await crud.UserDal(auth.db).create_data(data=data), refresh=auth.refresh) + return SuccessResponse(await crud.UserDal(auth.db).create_data(data=data)) @app.delete("/users/", summary="批量删除用户", description="软删除,删除后清空所关联的角色") @@ -46,7 +46,7 @@ async def delete_users(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAu elif 1 in ids.ids: return ErrorResponse("不能删除超级管理员用户") await crud.UserDal(auth.db).delete_datas(ids=ids.ids, v_soft=True, is_active=False) - return SuccessResponse("删除成功", refresh=auth.refresh) + return SuccessResponse("删除成功") @app.put("/users/{data_id}/", summary="更新用户信息") @@ -55,7 +55,7 @@ async def put_user( data: schemas.UserUpdate, auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.update"])) ): - return SuccessResponse(await crud.UserDal(auth.db).put_data(data_id, data), refresh=auth.refresh) + return SuccessResponse(await crud.UserDal(auth.db).put_data(data_id, data)) @app.get("/users/{data_id}/", summary="获取用户信息") @@ -66,32 +66,29 @@ async def get_user( model = models.VadminUser options = [joinedload(model.roles)] schema = schemas.UserOut - return SuccessResponse( - await crud.UserDal(auth.db).get_data(data_id, options, v_schema=schema), - refresh=auth.refresh - ) + return SuccessResponse(await crud.UserDal(auth.db).get_data(data_id, options, v_schema=schema)) @app.post("/user/current/reset/password/", summary="重置当前用户密码") async def user_current_reset_password(data: schemas.ResetPwd, auth: Auth = Depends(AllUserAuth())): - return SuccessResponse(await crud.UserDal(auth.db).reset_current_password(auth.user, data), refresh=auth.refresh) + return SuccessResponse(await crud.UserDal(auth.db).reset_current_password(auth.user, data)) @app.post("/user/current/update/info/", summary="更新当前用户基本信息") async def post_user_current_update_info(data: schemas.UserUpdateBaseInfo, auth: Auth = Depends(AllUserAuth())): - return SuccessResponse(await crud.UserDal(auth.db).update_current_info(auth.user, data), refresh=auth.refresh) + return SuccessResponse(await crud.UserDal(auth.db).update_current_info(auth.user, data)) @app.post("/user/current/update/avatar/", summary="更新当前用户头像") async def post_user_current_update_avatar(file: UploadFile, auth: Auth = Depends(AllUserAuth())): - return SuccessResponse(await crud.UserDal(auth.db).update_current_avatar(auth.user, file), refresh=auth.refresh) + return SuccessResponse(await crud.UserDal(auth.db).update_current_avatar(auth.user, file)) @app.get("/user/admin/current/info/", summary="获取当前管理员信息") async def get_user_admin_current_info(auth: Auth = Depends(FullAdminAuth())): result = schemas.UserOut.from_orm(auth.user).dict() - result["permissions"] = list(get_user_permissions(auth.user)) - return SuccessResponse(result, refresh=auth.refresh) + result["permissions"] = list(FullAdminAuth.get_user_permissions(auth.user)) + return SuccessResponse(result) @app.post("/user/export/query/list/to/excel/", summary="导出用户查询列表为excel") @@ -100,17 +97,17 @@ async def post_user_export_query_list( params: UserParams = Depends(), auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.export"])) ): - return SuccessResponse(await crud.UserDal(auth.db).export_query_list(header, params), refresh=auth.refresh) + return SuccessResponse(await crud.UserDal(auth.db).export_query_list(header, params)) @app.get("/user/download/import/template/", summary="下载最新批量导入用户模板") async def get_user_download_new_import_template(auth: Auth = Depends(AllUserAuth())): - return SuccessResponse(await crud.UserDal(auth.db).download_import_template(), refresh=auth.refresh) + return SuccessResponse(await crud.UserDal(auth.db).download_import_template()) @app.post("/import/users/", summary="批量导入用户") async def post_import_users(file: UploadFile, auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.import"]))): - return SuccessResponse(await crud.UserDal(auth.db).import_users(file), refresh=auth.refresh) + return SuccessResponse(await crud.UserDal(auth.db).import_users(file)) @app.post("/users/init/password/send/sms/", summary="初始化所选用户密码并发送通知短信") @@ -119,16 +116,13 @@ async def post_users_init_password( ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.reset"])) ): - return SuccessResponse( - await crud.UserDal(auth.db).init_password_send_sms(ids.ids, request.app.state.redis), - refresh=auth.refresh - ) + return SuccessResponse(await crud.UserDal(auth.db).init_password_send_sms(ids.ids, request.app.state.redis)) @app.put("/users/wx/server/openid/", summary="更新当前用户服务端微信平台openid") async def put_user_wx_server_openid(request: Request, code: str, auth: Auth = Depends(AllUserAuth())): result = await crud.UserDal(auth.db).update_wx_server_openid(code, auth.user, request.app.state.redis) - return SuccessResponse(result, refresh=auth.refresh) + return SuccessResponse(result) ########################################################### @@ -141,12 +135,12 @@ async def get_roles( ): datas = await crud.RoleDal(auth.db).get_datas(**params.dict()) count = await crud.RoleDal(auth.db).get_count(**params.to_count()) - return SuccessResponse(datas, count=count, refresh=auth.refresh) + return SuccessResponse(datas, count=count) @app.post("/roles/", summary="创建角色信息") async def create_role(role: schemas.RoleIn, auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create"]))): - return SuccessResponse(await crud.RoleDal(auth.db).create_data(data=role), refresh=auth.refresh) + return SuccessResponse(await crud.RoleDal(auth.db).create_data(data=role)) @app.delete("/roles/", summary="批量删除角色", description="硬删除, 如果存在用户关联则无法删除") @@ -154,7 +148,7 @@ async def delete_roles(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAu if 1 in ids.ids: return ErrorResponse("不能删除管理员角色") await crud.RoleDal(auth.db).delete_datas(ids.ids, v_soft=False) - return SuccessResponse("删除成功", refresh=auth.refresh) + return SuccessResponse("删除成功") @app.put("/roles/{data_id}/", summary="更新角色信息") @@ -165,12 +159,12 @@ async def put_role( ): if 1 == data_id: return ErrorResponse("不能修改管理员角色") - return SuccessResponse(await crud.RoleDal(auth.db).put_data(data_id, data), refresh=auth.refresh) + return SuccessResponse(await crud.RoleDal(auth.db).put_data(data_id, data)) @app.get("/roles/options/", summary="获取角色选择项") async def get_role_options(auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.create", "auth.user.update"]))): - return SuccessResponse(await crud.RoleDal(auth.db).get_select_datas(), refresh=auth.refresh) + return SuccessResponse(await crud.RoleDal(auth.db).get_select_datas()) @app.get("/roles/{data_id}/", summary="获取角色信息") @@ -181,10 +175,7 @@ async def get_role( model = models.VadminRole options = [joinedload(model.menus)] schema = schemas.RoleOut - return SuccessResponse( - await crud.RoleDal(auth.db).get_data(data_id, options, v_schema=schema), - refresh=auth.refresh - ) + return SuccessResponse(await crud.RoleDal(auth.db).get_data(data_id, options, v_schema=schema)) ########################################################### @@ -193,33 +184,33 @@ async def get_role( @app.get("/menus/", summary="获取菜单列表") async def get_menus(auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.list"]))): datas = await crud.MenuDal(auth.db).get_tree_list(mode=1) - return SuccessResponse(datas, refresh=auth.refresh) + return SuccessResponse(datas) @app.get("/menus/tree/options/", summary="获取菜单树选择项,添加/修改菜单时使用") async def get_menus_options(auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.create", "auth.menu.update"]))): datas = await crud.MenuDal(auth.db).get_tree_list(mode=2) - return SuccessResponse(datas, refresh=auth.refresh) + return SuccessResponse(datas) @app.get("/menus/role/tree/options/", summary="获取菜单列表树信息,角色权限使用") async def get_menus_treeselect( auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create", "auth.role.update"])) ): - return SuccessResponse(await crud.MenuDal(auth.db).get_tree_list(mode=3), refresh=auth.refresh) + return SuccessResponse(await crud.MenuDal(auth.db).get_tree_list(mode=3)) @app.post("/menus/", summary="创建菜单信息") async def create_menu(menu: schemas.Menu, auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.create"]))): if menu.parent_id: menu.alwaysShow = False - return SuccessResponse(await crud.MenuDal(auth.db).create_data(data=menu), refresh=auth.refresh) + return SuccessResponse(await crud.MenuDal(auth.db).create_data(data=menu)) @app.delete("/menus/", summary="批量删除菜单", description="硬删除, 如果存在角色关联则无法删除") async def delete_menus(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.delete"]))): await crud.MenuDal(auth.db).delete_datas(ids.ids, v_soft=False) - return SuccessResponse("删除成功", refresh=auth.refresh) + return SuccessResponse("删除成功") @app.put("/menus/{data_id}/", summary="更新菜单信息") @@ -227,7 +218,7 @@ async def put_menus( data_id: int, data: schemas.Menu, auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.update"])) ): - return SuccessResponse(await crud.MenuDal(auth.db).put_data(data_id, data), refresh=auth.refresh) + return SuccessResponse(await crud.MenuDal(auth.db).put_data(data_id, data)) @app.get("/menus/{data_id}/", summary="获取菜单信息") @@ -236,7 +227,7 @@ async def put_menus( auth: Auth = Depends(FullAdminAuth(permissions=["auth.menu.view", "auth.menu.update"])) ): schema = schemas.MenuSimpleOut - return SuccessResponse(await crud.MenuDal(auth.db).get_data(data_id, None, v_schema=schema), refresh=auth.refresh) + return SuccessResponse(await crud.MenuDal(auth.db).get_data(data_id, None, v_schema=schema)) @app.get("/role/menus/tree/{role_id}/", summary="获取菜单列表树信息以及角色菜单权限ID,角色权限使用") @@ -246,4 +237,4 @@ async def get_role_menu_tree( ): treeselect = await crud.MenuDal(auth.db).get_tree_list(mode=3) role_menu_tree = await crud.RoleDal(auth.db).get_role_menu_tree(role_id) - return SuccessResponse({"role_menu_tree": role_menu_tree, "menus": treeselect}, refresh=auth.refresh) + return SuccessResponse({"role_menu_tree": role_menu_tree, "menus": treeselect}) diff --git a/kinit-api/apps/vadmin/help/views.py b/kinit-api/apps/vadmin/help/views.py index d417283..665827a 100644 --- a/kinit-api/apps/vadmin/help/views.py +++ b/kinit-api/apps/vadmin/help/views.py @@ -29,42 +29,36 @@ async def get_issue_categorys(p: params.IssueCategoryParams = Depends(), auth: A schema = schemas.IssueCategoryListOut datas = await crud.IssueCategoryDal(auth.db).get_datas(**p.dict(), v_options=options, v_schema=schema) count = await crud.IssueCategoryDal(auth.db).get_count(**p.to_count()) - return SuccessResponse(datas, count=count, refresh=auth.refresh) + return SuccessResponse(datas, count=count) @app.get("/issue/categorys/options/", summary="获取类别选择项") async def get_issue_categorys_options(auth: Auth = Depends(AllUserAuth())): schema = schemas.IssueCategoryOptionsOut - return SuccessResponse( - await crud.IssueCategoryDal(auth.db).get_datas(limit=0, is_active=True, v_schema=schema), - refresh=auth.refresh - ) + return SuccessResponse(await crud.IssueCategoryDal(auth.db).get_datas(limit=0, is_active=True, v_schema=schema)) @app.post("/issue/categorys/", summary="创建类别") async def create_issue_category(data: schemas.IssueCategory, auth: Auth = Depends(AllUserAuth())): data.user_id = auth.user.id - return SuccessResponse(await crud.IssueCategoryDal(auth.db).create_data(data=data), refresh=auth.refresh) + return SuccessResponse(await crud.IssueCategoryDal(auth.db).create_data(data=data)) @app.delete("/issue/categorys/", summary="批量删除类别", description="硬删除") async def delete_issue_categorys(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())): await crud.IssueCategoryDal(auth.db).delete_datas(ids=ids.ids, v_soft=False) - return SuccessResponse("删除成功", refresh=auth.refresh) + return SuccessResponse("删除成功") @app.put("/issue/categorys/{data_id}/", summary="更新类别信息") async def put_issue_category(data_id: int, data: schemas.IssueCategory, auth: Auth = Depends(AllUserAuth())): - return SuccessResponse(await crud.IssueCategoryDal(auth.db).put_data(data_id, data), refresh=auth.refresh) + return SuccessResponse(await crud.IssueCategoryDal(auth.db).put_data(data_id, data)) @app.get("/issue/categorys/{data_id}/", summary="获取类别信息") async def get_issue_category(data_id: int, auth: Auth = Depends(AllUserAuth())): schema = schemas.IssueCategorySimpleOut - return SuccessResponse( - await crud.IssueCategoryDal(auth.db).get_data(data_id, v_schema=schema), - refresh=auth.refresh - ) + return SuccessResponse(await crud.IssueCategoryDal(auth.db).get_data(data_id, v_schema=schema)) @app.get("/issue/categorys/platform/{platform}/", summary="获取平台中的常见问题类别列表") @@ -87,24 +81,24 @@ async def get_issues(p: params.IssueParams = Depends(), auth: Auth = Depends(All schema = schemas.IssueListOut datas = await crud.IssueDal(auth.db).get_datas(**p.dict(), v_options=options, v_schema=schema) count = await crud.IssueDal(auth.db).get_count(**p.to_count()) - return SuccessResponse(datas, count=count, refresh=auth.refresh) + return SuccessResponse(datas, count=count) @app.post("/issues/", summary="创建问题") async def create_issue(data: schemas.Issue, auth: Auth = Depends(AllUserAuth())): data.user_id = auth.user.id - return SuccessResponse(await crud.IssueDal(auth.db).create_data(data=data), refresh=auth.refresh) + return SuccessResponse(await crud.IssueDal(auth.db).create_data(data=data)) @app.delete("/issues/", summary="批量删除问题", description="硬删除") async def delete_issues(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())): await crud.IssueDal(auth.db).delete_datas(ids=ids.ids, v_soft=False) - return SuccessResponse("删除成功", refresh=auth.refresh) + return SuccessResponse("删除成功") @app.put("/issues/{data_id}/", summary="更新问题信息") async def put_issue(data_id: int, data: schemas.Issue, auth: Auth = Depends(AllUserAuth())): - return SuccessResponse(await crud.IssueDal(auth.db).put_data(data_id, data), refresh=auth.refresh) + return SuccessResponse(await crud.IssueDal(auth.db).put_data(data_id, data)) @app.get("/issues/{data_id}/", summary="获取问题信息") diff --git a/kinit-api/apps/vadmin/record/views.py b/kinit-api/apps/vadmin/record/views.py index 317104e..a99b944 100644 --- a/kinit-api/apps/vadmin/record/views.py +++ b/kinit-api/apps/vadmin/record/views.py @@ -23,22 +23,25 @@ app = APIRouter() async def get_record_login(p: LoginParams = Depends(), auth: Auth = Depends(AllUserAuth())): datas = await crud.LoginRecordDal(auth.db).get_datas(**p.dict()) count = await crud.LoginRecordDal(auth.db).get_count(**p.to_count()) - return SuccessResponse(datas, count=count, refresh=auth.refresh) + return SuccessResponse(datas, count=count) @app.get("/operations/", summary="获取操作日志列表") -async def get_record_operation(p: OperationParams = Depends(), db: DatabaseManage = Depends(get_database), - auth: Auth = Depends(AllUserAuth())): +async def get_record_operation( + p: OperationParams = Depends(), + db: DatabaseManage = Depends(get_database), + auth: Auth = Depends(AllUserAuth()) +): count = await db.get_count("operation_record", **p.to_count()) datas = await db.get_datas("operation_record", v_schema=schemas.OpertionRecordSimpleOut, **p.dict()) - return SuccessResponse(datas, count=count, refresh=auth.refresh) + return SuccessResponse(datas, count=count) @app.get("/sms/send/list/", summary="获取短信发送列表") async def get_sms_send_list(p: SMSParams = Depends(), auth: Auth = Depends(AllUserAuth())): datas = await crud.SMSSendRecordDal(auth.db).get_datas(**p.dict()) count = await crud.SMSSendRecordDal(auth.db).get_count(**p.to_count()) - return SuccessResponse(datas, count=count, refresh=auth.refresh) + return SuccessResponse(datas, count=count) ########################################################### @@ -46,4 +49,4 @@ async def get_sms_send_list(p: SMSParams = Depends(), auth: Auth = Depends(AllUs ########################################################### @app.get("/analysis/user/login/distribute/", summary="获取用户登录分布情况列表") async def get_user_login_distribute(auth: Auth = Depends(AllUserAuth())): - return SuccessResponse(await crud.LoginRecordDal(auth.db).get_user_distribute(), refresh=auth.refresh) + return SuccessResponse(await crud.LoginRecordDal(auth.db).get_user_distribute()) diff --git a/kinit-api/apps/vadmin/system/views.py b/kinit-api/apps/vadmin/system/views.py index 3893ce8..9f66a0c 100644 --- a/kinit-api/apps/vadmin/system/views.py +++ b/kinit-api/apps/vadmin/system/views.py @@ -31,18 +31,18 @@ app = APIRouter() async def get_dict_types(p: DictTypeParams = Depends(), auth: Auth = Depends(AllUserAuth())): datas = await crud.DictTypeDal(auth.db).get_datas(**p.dict()) count = await crud.DictTypeDal(auth.db).get_count(**p.to_count()) - return SuccessResponse(datas, count=count, refresh=auth.refresh) + return SuccessResponse(datas, count=count) @app.post("/dict/types/", summary="创建字典类型") async def create_dict_types(data: schemas.DictType, auth: Auth = Depends(AllUserAuth())): - return SuccessResponse(await crud.DictTypeDal(auth.db).create_data(data=data), refresh=auth.refresh) + return SuccessResponse(await crud.DictTypeDal(auth.db).create_data(data=data)) @app.delete("/dict/types/", summary="批量删除字典类型") async def delete_dict_types(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())): await crud.DictTypeDal(auth.db).delete_datas(ids=ids.ids) - return SuccessResponse("删除成功", refresh=auth.refresh) + return SuccessResponse("删除成功") @app.post("/dict/types/details/", summary="获取多个字典类型下的字典元素列表") @@ -51,17 +51,17 @@ async def post_dicts_details( dict_types: List[str] = Body(None, title="字典元素列表", description="查询字典元素列表") ): datas = await crud.DictTypeDal(auth.db).get_dicts_details(dict_types) - return SuccessResponse(datas, refresh=auth.refresh) + return SuccessResponse(datas) @app.get("/dict/types/options/", summary="获取字典类型选择项") async def get_dicts_options(auth: Auth = Depends(AllUserAuth())): - return SuccessResponse(await crud.DictTypeDal(auth.db).get_select_datas(), refresh=auth.refresh) + return SuccessResponse(await crud.DictTypeDal(auth.db).get_select_datas()) @app.put("/dict/types/{data_id}/", summary="更新字典类型") async def put_dict_types(data_id: int, data: schemas.DictType, auth: Auth = Depends(AllUserAuth())): - return SuccessResponse(await crud.DictTypeDal(auth.db).put_data(data_id, data), refresh=auth.refresh) + return SuccessResponse(await crud.DictTypeDal(auth.db).put_data(data_id, data)) @app.get("/dict/types/{data_id}/", summary="获取字典类型详细") @@ -78,7 +78,7 @@ async def get_dict_type(data_id: int, auth: Auth = Depends(AllUserAuth())): ########################################################### @app.post("/dict/details/", summary="创建字典元素") async def create_dict_details(data: schemas.DictDetails, auth: Auth = Depends(AllUserAuth())): - return SuccessResponse(await crud.DictDetailsDal(auth.db).create_data(data=data), refresh=auth.refresh) + return SuccessResponse(await crud.DictDetailsDal(auth.db).create_data(data=data)) @app.get("/dict/details/", summary="获取单个字典类型下的字典元素列表,分页") @@ -87,18 +87,18 @@ async def get_dict_details(params: DictDetailParams = Depends(), auth: Auth = De return ErrorResponse(msg="未获取到字典类型!") datas = await crud.DictDetailsDal(auth.db).get_datas(**params.dict()) count = await crud.DictDetailsDal(auth.db).get_count(**params.to_count()) - return SuccessResponse(datas, count=count, refresh=auth.refresh) + return SuccessResponse(datas, count=count) @app.delete("/dict/details/", summary="批量删除字典元素", description="硬删除") async def delete_dict_details(ids: IdList = Depends(), auth: Auth = Depends(AllUserAuth())): await crud.DictDetailsDal(auth.db).delete_datas(ids.ids, v_soft=False) - return SuccessResponse("删除成功", refresh=auth.refresh) + return SuccessResponse("删除成功") @app.put("/dict/details/{data_id}/", summary="更新字典元素") async def put_dict_details(data_id: int, data: schemas.DictDetails, auth: Auth = Depends(AllUserAuth())): - return SuccessResponse(await crud.DictDetailsDal(auth.db).put_data(data_id, data), refresh=auth.refresh) + return SuccessResponse(await crud.DictDetailsDal(auth.db).put_data(data_id, data)) @app.get("/dict/details/{data_id}/", summary="获取字典元素详情") @@ -142,23 +142,17 @@ async def sms_send(request: Request, telephone: str): ########################################################### @app.get("/settings/tabs/", summary="获取系统配置标签列表") async def get_settings_tabs(classify: str, auth: Auth = Depends(FullAdminAuth())): - return SuccessResponse( - await crud.SettingsTabDal(auth.db).get_datas(limit=0, classify=classify), - refresh=auth.refresh - ) + return SuccessResponse(await crud.SettingsTabDal(auth.db).get_datas(limit=0, classify=classify)) @app.get("/settings/tabs/values/", summary="获取系统配置标签下的信息") async def get_settings_tabs_values(tab_id: int, auth: Auth = Depends(FullAdminAuth())): - return SuccessResponse(await crud.SettingsDal(auth.db).get_tab_values(tab_id=tab_id), refresh=auth.refresh) + return SuccessResponse(await crud.SettingsDal(auth.db).get_tab_values(tab_id=tab_id)) @app.put("/settings/tabs/values/", summary="更新系统配置信息") async def put_settings_tabs_values(request: Request, datas: dict = Body(...), auth: Auth = Depends(FullAdminAuth())): - return SuccessResponse( - await crud.SettingsDal(auth.db).update_datas(datas, request.app.state.redis), - refresh=auth.refresh - ) + return SuccessResponse(await crud.SettingsDal(auth.db).update_datas(datas, request.app.state.redis)) @app.get("/settings/base/config/", summary="获取系统基础配置", description="每次进入系统中时使用") @@ -168,15 +162,9 @@ async def get_setting_base_config(db: AsyncSession = Depends(db_getter)): @app.get("/settings/privacy/", summary="获取隐私协议") async def get_settings_privacy(auth: Auth = Depends(FullAdminAuth())): - return SuccessResponse( - (await crud.SettingsDal(auth.db).get_data(config_key="web_privacy")).config_value, - refresh=auth.refresh - ) + return SuccessResponse((await crud.SettingsDal(auth.db).get_data(config_key="web_privacy")).config_value) @app.get("/settings/agreement/", summary="获取用户协议") async def get_settings_agreement(auth: Auth = Depends(FullAdminAuth())): - return SuccessResponse( - (await crud.SettingsDal(auth.db).get_data(config_key="web_agreement")).config_value, - refresh=auth.refresh - ) + return SuccessResponse((await crud.SettingsDal(auth.db).get_data(config_key="web_agreement")).config_value) diff --git a/kinit-api/core/middleware.py b/kinit-api/core/middleware.py index bc5056b..108af7a 100644 --- a/kinit-api/core/middleware.py +++ b/kinit-api/core/middleware.py @@ -13,7 +13,6 @@ import datetime import json import time from fastapi import Request, Response -from core.exception import CustomException from core.logger import logger from fastapi import FastAPI from fastapi.routing import APIRoute @@ -21,10 +20,6 @@ from user_agents import parse from application.settings import OPERATION_RECORD_METHOD, MONGO_DB_ENABLE, IGNORE_OPERATION_FUNCTION,\ DEMO_WHITE_LIST_PATH, DEMO from core.mongo import get_database - - -# 记录请求日志 -from utils import status from utils.response import ErrorResponse @@ -137,3 +132,18 @@ def register_demo_env_middleware(app: FastAPI): if DEMO and request.method != "GET" and path not in DEMO_WHITE_LIST_PATH: return ErrorResponse(msg="演示环境,禁止操作") return await call_next(request) + + +def register_jwt_refresh_middleware(app: FastAPI): + """ + JWT刷新中间件 + :param app: + :return: + """ + + @app.middleware("http") + async def jwt_refresh_middleware(request: Request, call_next): + response = await call_next(request) + refresh = request.scope.get('refresh', 0) + response.headers["refresh"] = str(refresh) + return response diff --git a/kinit-api/requirements.txt b/kinit-api/requirements.txt index 7899e11..3a335d0 100644 --- a/kinit-api/requirements.txt +++ b/kinit-api/requirements.txt @@ -29,7 +29,7 @@ cryptography==38.0.3 dnspython==2.2.1 ecdsa==0.18.0 et-xmlfile==1.1.0 -fastapi==0.94.1 +fastapi==0.95.0 frozenlist==1.3.3 greenlet==2.0.1 gunicorn==20.1.0 diff --git a/kinit-uni/common/request/request.js b/kinit-uni/common/request/request.js index f533228..c25d4b4 100644 --- a/kinit-uni/common/request/request.js +++ b/kinit-uni/common/request/request.js @@ -42,7 +42,7 @@ http.interceptors.response.use( // 获取错误信息 const msg = res.data.message || errorCode[code] || errorCode['default'] // 是否刷新token - const refresh = res.data.refresh || false + const refresh = res.header.refresh if (code === 500) { toast(msg) return Promise.reject(new Error(msg)) @@ -58,7 +58,7 @@ http.interceptors.response.use( toast(msg) return Promise.reject('error') } else if (code === 200) { - if (refresh) { + if (refresh === '1') { // 因token快过期,刷新token refreshToken().then((res) => { setToken(`${res.data.token_type} ${res.data.access_token}`)