diff --git a/vue-fastapi-backend/module_admin/controller/metasecurity_controller.py b/vue-fastapi-backend/module_admin/controller/metasecurity_controller.py index b8067bc..120f798 100644 --- a/vue-fastapi-backend/module_admin/controller/metasecurity_controller.py +++ b/vue-fastapi-backend/module_admin/controller/metasecurity_controller.py @@ -7,7 +7,7 @@ from config.get_db import get_db from module_admin.entity.vo.user_vo import CurrentUserModel from module_admin.annotation.log_annotation import Log from module_admin.aspect.interface_auth import CheckUserInterfaceAuth -from module_admin.entity.vo.metasecurity_vo import MetaSecurityColModel, MetaSecurityRowModel, MetaSecurityColPageQueryModel, MetaSecurityRowPageQueryModel, DeleteMetaSecurityModel +from module_admin.entity.vo.metasecurity_vo import MetaSecurityColModel, MetaSecurityApiModel,MetaSecurityRowModel, MetaSecurityColPageQueryModel, MetaSecurityRowPageQueryModel, DeleteMetaSecurityModel from module_admin.service.metasecurity_service import MetaSecurityService from module_admin.service.login_service import LoginService from utils.common_util import bytes2file_response @@ -176,3 +176,15 @@ async def query_meta_security_row_detail( row_detail_result = await MetaSecurityService.get_meta_security_row_by_id_services(query_db, row_id) logger.info(f'获取row_id为{row_id}的行配置信息成功') return ResponseUtil.success(data=row_detail_result) + + +# 数据安全接口 +@metaSecurityController.get( + '/getMetaSercuitybysql' +) +@ValidateFields(validate_model='apiModel') +async def getMetaSercuitybysql(request: Request, apiModel: MetaSecurityApiModel=Depends(MetaSecurityApiModel), query_db: AsyncSession = Depends(get_db)): + config_detail_result = await MetaSecurityService.getMetaSercuitybysql(request,query_db, apiModel) + logger.info(f'获取config_id为{apiModel}的信息成功') + + return ResponseUtil.success(data=config_detail_result) diff --git a/vue-fastapi-backend/module_admin/dao/metaSecurity_dao.py b/vue-fastapi-backend/module_admin/dao/metaSecurity_dao.py index e99100e..d1def78 100644 --- a/vue-fastapi-backend/module_admin/dao/metaSecurity_dao.py +++ b/vue-fastapi-backend/module_admin/dao/metaSecurity_dao.py @@ -27,6 +27,8 @@ class MetaSecurityDao: filters.append(MetaSecurityCol.obj_value.like(f"%{query_object.obj_value}%")) if query_object.dbTName: filters.append(MetaSecurityCol.dbTName.like(f"%{query_object.dbTName}%")) + if query_object.obj_name: + filters.append(MetaSecurityCol.obj_name.like(f"%{query_object.obj_name}%")) if query_object.dbSName: filters.append(MetaSecurityCol.dbSName.like(f"%{query_object.dbSName}%")) if query_object.dbRID: @@ -45,7 +47,6 @@ class MetaSecurityDao: return col_list - @classmethod async def get_meta_security_row_list(cls, db: AsyncSession, query_object: MetaSecurityRowModel, is_page: bool = False): """ @@ -65,6 +66,8 @@ class MetaSecurityDao: filters.append(MetaSecurityRow.dbTName.like(f"%{query_object.dbTName}%")) if query_object.dbSName: filters.append(MetaSecurityRow.dbSName.like(f"%{query_object.dbSName}%")) + if query_object.obj_name: + filters.append(MetaSecurityRow.obj_name.like(f"%{query_object.obj_name}%")) if query_object.dbRID: filters.append(MetaSecurityRow.dbRID==query_object.dbRID) # 构建查询语句 @@ -79,7 +82,37 @@ class MetaSecurityDao: db, query, query_object.page_num, query_object.page_size, is_page ) return row_list - + + @classmethod + async def get_api_col_list(cls, db: AsyncSession, dbRId: int,dbTname:str,objType:str,objValue:str): + colList = ( + await db.execute( + select(MetaSecurityCol) + .where( + (MetaSecurityCol.isStop == 0) & + (MetaSecurityCol.dbRID == dbRId) & + (MetaSecurityCol.obj_type == objType) & + (MetaSecurityCol.dbTName == dbTname) & + (MetaSecurityCol.obj_value == objValue) + ) + ) + ).scalars().all() + return colList + @classmethod + async def get_api_row_list(cls, db: AsyncSession, dbRId: int,dbTname:str,objType:str,objValue:str): + colList = ( + await db.execute( + select(MetaSecurityRow) + .where( + (MetaSecurityRow.isStop == 0) & + (MetaSecurityRow.dbRID == dbRId) & + (MetaSecurityRow.obj_type == objType) & + (MetaSecurityRow.dbTName == dbTname) & + (MetaSecurityRow.obj_value == objValue) + ) + ) + ).scalars().all() + return colList @classmethod async def get_meta_security_col_by_id(cls, db: AsyncSession, colId: str): diff --git a/vue-fastapi-backend/module_admin/entity/do/metasecurity_do.py b/vue-fastapi-backend/module_admin/entity/do/metasecurity_do.py index d412604..3bc4905 100644 --- a/vue-fastapi-backend/module_admin/entity/do/metasecurity_do.py +++ b/vue-fastapi-backend/module_admin/entity/do/metasecurity_do.py @@ -17,11 +17,12 @@ class MetaSecurityCol(Base): dbRName = Column(String(50), default=None, comment='数据源名称') dbRID = Column(Integer, nullable=True, default=None, comment='数据源ID') dbSName = Column(String(50), default=None, comment='模式名称') - dbTName = Column(String(50), default=None, comment='表名称') - dbCName = Column(String(50), default=None, comment='字段名称') + dbTName = Column(String(200), default=None, comment='表名称') + dbCName = Column(String(500), default=None, comment='字段名称') ctrl_type = Column(String(1), default=None, comment='控制类型(0:反向 1:正向)') obj_type = Column(String(1), default=None, comment='对象类型(0:角色 1:用户)') obj_value = Column(Integer, default=None, comment='角色值') + obj_name = Column(String(50), default=None, comment='字段名称') isStop = Column(Boolean, nullable=True, default=None, comment='是否停用(0:运行 1:停用)') @@ -46,6 +47,7 @@ class MetaSecurityRow(Base): ctrl_type = Column(String(1), default=None, comment='控制类型(0:控制值 1:控制表 2:控制程序)') obj_type = Column(String(1), default=None, comment='对象类型(0:角色 1:用户)') obj_value = Column(String(50), default=None, comment='角色值') + obj_name = Column(String(50), default=None, comment='字段名称') isStop = Column(Boolean, nullable=True, default=None, comment='是否停用(0:运行 1:停用)') ctrl_value = Column(String(100), default=None, comment='控制值') ctrl_table = Column(String(100), default=None, comment='控制表') diff --git a/vue-fastapi-backend/module_admin/entity/vo/metasecurity_vo.py b/vue-fastapi-backend/module_admin/entity/vo/metasecurity_vo.py index c775ea0..aba191f 100644 --- a/vue-fastapi-backend/module_admin/entity/vo/metasecurity_vo.py +++ b/vue-fastapi-backend/module_admin/entity/vo/metasecurity_vo.py @@ -4,6 +4,7 @@ from datetime import datetime from module_admin.annotation.pydantic_annotation import as_query from pydantic import BaseModel, ConfigDict, Field from pydantic.alias_generators import to_camel +from pydantic_validation_decorator import NotBlank, Size class CrudMetaSecurityModel(BaseModel): @@ -29,6 +30,7 @@ class MetaSecurityRowModel(BaseModel): ctrl_type: Optional[str] = None # control type (e.g., '0', '1', '2') obj_type: Optional[str] = None # object type (e.g., '0', '1') obj_value: Optional[object] = None + obj_name: Optional[object] = None isStop: Optional[bool] = None ctrl_value: Optional[str] = None ctrl_table: Optional[str] = None @@ -55,6 +57,7 @@ class MetaSecurityColModel(BaseModel): ctrl_type: Optional[str] = None # control type (e.g., '0', '1') obj_type: Optional[str] = None # object type (e.g., '0', '1') obj_value: Optional[object] = None + obj_name: Optional[object] = None isStop: Optional[bool] = None @as_query class MetaSecurityRowPageQueryModel(MetaSecurityRowModel): @@ -77,3 +80,32 @@ class DeleteMetaSecurityModel(BaseModel): """ model_config = ConfigDict(alias_generator=to_camel) metaSecurity_ids: str = Field(description='需要删除的参数主键') +class MetaSecurityApiModel(BaseModel): + """ + 数据源行控制表对应Pydantic模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + dbRId: Optional[int] = None + username: Optional[str] = Field(default=None, description='用户名称') + password: Optional[str] = Field(default=None, description='用户密码') + tableName: Optional[str] = Field(default=None, description='表名') + sqlStr: Optional[str] = Field(default=None, description='sql') + + @NotBlank(field_name='username', message='用户名称不能为空') + @Size(field_name='username', min_length=0, max_length=100, message='用户名称长度不能超过100个字符') + def get_username(self): + return self.username + @NotBlank(field_name='password', message='用户密码不能为空') + def get_password(self): + return self.password + @NotBlank(field_name='tableName', message='表名不能为空') + def get_tableName(self): + return self.tableName + @NotBlank(field_name='sqlStr', message='sql不能为空') + def get_sqlStr(self): + return self.username + def validate_fields(self): + self.get_username() + self.get_password() + self.get_tableName() + self.get_sqlStr() diff --git a/vue-fastapi-backend/module_admin/service/metasecurity_service.py b/vue-fastapi-backend/module_admin/service/metasecurity_service.py index 94da370..6ff7298 100644 --- a/vue-fastapi-backend/module_admin/service/metasecurity_service.py +++ b/vue-fastapi-backend/module_admin/service/metasecurity_service.py @@ -1,14 +1,18 @@ from fastapi import Request from sqlalchemy.ext.asyncio import AsyncSession -from typing import List -from config.constant import CommonConstant from exceptions.exception import ServiceException from module_admin.dao.metaSecurity_dao import MetaSecurityDao from module_admin.entity.vo.common_vo import CrudResponseModel -from module_admin.entity.vo.metasecurity_vo import MetaSecurityColModel, MetaSecurityRowModel,DeleteMetaSecurityModel +from module_admin.entity.vo.metasecurity_vo import MetaSecurityColModel, MetaSecurityRowModel,DeleteMetaSecurityModel,MetaSecurityApiModel from utils.common_util import CamelCaseUtil import uuid - +from module_admin.dao.login_dao import login_by_account +from module_admin.dao.user_dao import UserDao +from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy import text +import json class MetaSecurityService: """ @@ -88,17 +92,19 @@ class MetaSecurityService: :return: 新增列配置校验结果 """ try: - if isinstance(page_object.obj_value, str) and page_object.obj_value: + if isinstance(page_object.obj_value, str) and page_object.obj_value: obj_values = page_object.obj_value.split(",") - for value in obj_values: + obj_names = page_object.obj_name.split(",") + for value, name in zip(obj_values, obj_names): # 创建新的 page_object 实例,避免修改原始对象 new_page_object = MetaSecurityColModel(**page_object.model_dump(by_alias=True)) new_page_object.obj_value = value.strip() # 去除空格并赋值 + new_page_object.obj_name = name.strip() # 去除空格并赋值 new_page_object.colId = str(uuid.uuid4()) # 调用 DAO 方法插入数据 await MetaSecurityDao.add_meta_security_col(query_db, new_page_object) - await query_db.commit() - return CrudResponseModel(is_success=True, message='新增列配置成功') + await query_db.commit() + return CrudResponseModel(is_success=True, message='新增列配置成功') except Exception as e: await query_db.rollback() raise e @@ -116,10 +122,12 @@ class MetaSecurityService: try: if isinstance(page_object.obj_value, str) and page_object.obj_value: obj_values = page_object.obj_value.split(",") - for value in obj_values: + obj_names = page_object.obj_name.split(",") + for value, name in zip(obj_values, obj_names): # 创建新的 page_object 实例,避免修改原始对象 new_page_object = MetaSecurityRowModel(**page_object.model_dump(by_alias=True)) new_page_object.obj_value = value.strip() # 去除空格并赋值 + new_page_object.obj_name = name.strip() # 去除空格并赋值 new_page_object.rowId = str(uuid.uuid4()) # 调用 DAO 方法插入数据 await MetaSecurityDao.add_meta_security_row(query_db, new_page_object) @@ -266,5 +274,100 @@ class MetaSecurityService: raise e else: raise ServiceException(message='传入行配置ID为空') - - \ No newline at end of file + @classmethod + async def getMetaSercuitybysql(cls, request: Request, query_db: AsyncSession, page_object: MetaSecurityApiModel): + #1.校验用户 + if not page_object.username: + raise ServiceException(data='', message='用户名不能为空!') + user = await login_by_account(query_db, page_object.username) + if not user: + raise ServiceException(data='', message='用户不存在') + if not page_object.password == user[0].password: + raise ServiceException(data='', message='用户密码错误!') + query_user = await UserDao.get_user_by_id(query_db, user_id=user[0].user_id) + role_id_list = [item.role_id for item in query_user.get('user_role_info')] + #2.查询用户及该用户角色下的所有行列配置 + # 列配置 + user_col_list=await MetaSecurityDao.get_api_col_list(query_db, page_object.dbRId,page_object.tableName, '0',user[0].user_id) + role_col_list = [] + for role_id in role_id_list: + role_cols = await MetaSecurityDao.get_api_col_list( + query_db, page_object.dbRId, page_object.tableName, '1', role_id + ) + role_col_list.extend(role_cols) # 将每个角色的列配置合并到列表中 + + # 行配置 + user_row_list=await MetaSecurityDao.get_api_row_list(query_db, page_object.dbRId,page_object.tableName, '0',user[0].user_id) + role_row_list = [] + for role_id in role_id_list: + role_rows = await MetaSecurityDao.get_api_row_list( + query_db, page_object.dbRId, page_object.tableName, '1', role_id + ) + role_row_list.extend(role_rows) # 将每个角色的行配置合并到列表中 + result = { + "user_col_list": user_col_list, + "role_col_list": role_col_list, + "role_row_list": role_row_list, + "user_row_list": user_row_list + } + # return result + #3.根据行列配置控制原始sql + #4.测试数据源连接是否正常 + dataParams ={"user":"dbf","password":"1q2w3e4r","address":"jdbc:mysql://47.113.147.166:3306","database":"dash_test_w","jdbcUrl":"jdbc:mysql://47.113.147.166:3306/dash_test_w","driverClassName":"com.mysql.cj.jdbc.Driver","validationQuery":"select 1"} + dbConnent = cls.get_db_engine('mysql',dataParams) + query = "SELECT * FROM msq_table_constraints" + result = await cls.execute_sql(dbConnent, query) + return result + #5.执行原始sql + #6.执行控制后的sql + #7.执行结果 + + def get_db_engine(db_type: str, db_params: dict): + try: + if db_type == "mysql": + address = db_params['address'] + if address.startswith("jdbc:mysql://"): + # 去掉 jdbc:mysql:// 部分 + address = address[len("jdbc:mysql://"):] + db_params['address'] = address + conn_str=f"mysql+aiomysql://{db_params['user']}:{db_params['password']}@{db_params['address']}/{db_params['database']}" + print(f"数据库连接字符串: {conn_str}") # 输出调试信息 + return create_async_engine(conn_str) + elif db_type == "postgresql": + return create_async_engine(f"postgresql+asyncpg://{db_params['user']}:{db_params['password']}@{db_params['address']}/{db_params['database']}") + # 你可以根据需求添加更多数据库类型 + else: + raise ValueError("不支持的数据库类型") + except SQLAlchemyError as e: + # 捕获SQLAlchemy相关的数据库连接错误 + raise ConnectionError(f"数据库连接失败: {e}") + except Exception as e: + # 捕获其他非预期的错误 + raise RuntimeError(f"连接过程中发生了未知错误: {e}") + @classmethod + async def execute_sql(cls, dbConnent, sql_query: str,sql_type: str): + # 创建异步会话 + async with dbConnent.begin(): + # 获取会话对象 + async_session = sessionmaker( + dbConnent, class_=AsyncSession, expire_on_commit=False + ) + async with async_session() as session: + try: + # 执行原始SQL查询 + query = text(sql_query) + result = await session.execute(query) + + # 获取所有结果 + rows = result.fetchall() + + # 获取列名 + columns = result.keys() + + # 将每一行转化为字典,键为列名 + result_dict = [dict(zip(columns, row)) for row in rows] + + # 转换为 JSON 字符串 + return json.dumps(result_dict, ensure_ascii=False, indent=4) + except SQLAlchemyError as e: + raise RuntimeError(f"{sql_type}执行 SQL 查询时发生错误: {e}") \ No newline at end of file diff --git a/vue-fastapi-frontend/src/views/meta/metasercurity/MetaSecurityCol.vue b/vue-fastapi-frontend/src/views/meta/metasercurity/MetaSecurityCol.vue index 98420a3..ce495ea 100644 --- a/vue-fastapi-frontend/src/views/meta/metasercurity/MetaSecurityCol.vue +++ b/vue-fastapi-frontend/src/views/meta/metasercurity/MetaSecurityCol.vue @@ -38,19 +38,21 @@ - + - + - + - + 搜索 + 重置 + @@ -122,7 +124,13 @@ - + + + - + + +