flask项目代码解析
.
.
.
.
check_permit_role_api 装饰器函数
# 两层校验
# 既对token校验,也通过用户角色来判断,该用户是否有执行该api的权限
-----------------------------------------------
# 对token的校验,如果是允许的永久token,就不进行role_id的校验了,直接让通过
# 如果是普通的token,就进行正常的token校验,如果没有问题
# 再通过该token,获取对应的用户对象,然后得到该用户对象的role_id
# 如果该用户对象的role_id,在我们设置的强制允许通过的角色id列表中pass_role_id_list中,也直接让通过,不校验了
# 最后如果是普通token,我们不设强制允许通过的角色id列表,或用户对应的role_id不在强制允许通过的角色id列表中
# 那就只能老老实实的到角色与api多对多的表里面,查看该角色是否有对应的api关系,有就说明该角色有改api的权限,也让通过
-----------------------------------------------
def check_permit_role_api(api_id: int, pass_token = None, pass_role_id_list: list = None):
def _check_permit_role_api(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 检查headers中是否传入token
if 'token' not in request.headers:
return jsonify_operation_msg(False, 'headers中没有找到token')
if pass_token:
# 判断pass_token是否是列表类型
if isinstance(pass_token, list):
if request.headers['token'] in pass_token:
return func()
# 默认传入为字符串,检查是否为可通过token,是则放行
if request.headers['token'] == pass_token:
return func()
# 根据请求头里token,获取session对象
session: AuthSession = db.session.query(AuthSession).where(AuthSession.token == request.headers['token'],
AuthSession.expire_time >= datetime.now()).first()
# 检查是否存在有效期内的token
if session is None:
return jsonify_operation_msg(False, 'token无效,无法调用模块', code=999)
# 对快到期的token进行检查,如果快到期则延长
if session.expire_time <= (datetime.now() + timedelta(seconds=token_expire_time / 2)):
try:
session.expire_time = datetime.now() + timedelta(seconds=token_expire_time)
db.session.commit()
except:
db.session.rollback()
# 根据session对象,获取用户对象
user: AuthUser = db.session.query(AuthUser).where(AuthUser.id == session.user_id).first()
if pass_role_id_list:
# 如果用户本身就是根管理员,直接放行
if user.role_id in pass_role_id_list:
return func()
# 角色与api多对多的表里面,查看该角色是否有对应的api关系,有就说明该角色有改api的权限,也让通过
permit_role_api: AuthPermitRoleApi = db.session.query(AuthPermitRoleApi) \
.select_from(AuthPermitRoleApi) \
.outerjoin(AuthUser, AuthPermitRoleApi.role_id == AuthUser.role_id) \
.filter(AuthUser.id == user.id, AuthPermitRoleApi.api_id == api_id) \
.first()
if permit_role_api is None:
return jsonify_operation_msg(False, '用户角色无该权限,无法调用模块')
return func()
wrapper.__name__ = func.__name__
return wrapper
return _check_permit_role_api
.
.
.
.
.
.
.
.
展示当前用户所能看到的api资源树,下每一个api对象信息,并且展示出要查的角色,有没有这些api的权限
from sqlalchemy.orm import aliased
# -----------------------------------------------------------
# 该递归函数
# 目的就是确定当前的api对象,有没有子api对象,如果没有,返回一个空列表,那么就不再递归了
# 如果有子api对象,那么就返回一个列表套字典的数据,列表套字典的数据里,每一个字典里,都包含一个子api对象的数据
# 并且同时判断要查的角色对象是否有 当前api的每一个子api对象的权限!!!
def find_children_detail_with_role_permit(self, role: AuthRole, user: AuthUser = None):
# self 是查出来的api对象
# 要查的角色对象与当前用户对象
# 如果当前用户没有用SUPER_ADMIN_TOKEN,并且该用户不是超级管理员,走如下逻辑
if user and user.role_id != 1:
# 先给 AuthPermitRoleApi表起了两个别名
user_api_permit: AuthPermitRoleApi = aliased(AuthPermitRoleApi)
role_api_permit: AuthPermitRoleApi = aliased(AuthPermitRoleApi)
# 返回值类型为 List[Tuple[AuthResourceApiExt, AuthPermitRoleApi]]
# api表作为起始表,连接api角色多对多表,并且通过起别名的方式,连这个表连两次
# .outerjoin(role_api_permit,(user_api_permit.api_id == role_api_permit.api_id) & (role_api_permit.role_id == role.id))
# 再次使用外连接,将 user_api_permit 表与 role_api_permit 表连接,连接条件是它们的 api_id 字段相等,
# 并且加了另一个连表条件 role_api_permit 表中的 role_id 字段等于某个 role 对象的 id 字段。 这样如果没连到role_api_permit表,行对象就是一个空了!!!
# 对查询结果进行过滤,过滤出符合 api表的parent_id等于 当前api对象id,且api与角色多对多表里角色id等于用户对象的role_id 的api对象来
# 也就是说过滤出当前api对象的子api对象,并且这些子api对象还要是,当前用户的角色所拥有的!!!
# 而且还通过多连的那一张 角色与api多对多表,的表对象的有无,知道了要查的角色对象是否拥有该子api对象的权限!!!
children_with_permit = db.session.query(AuthResourceApiExt, role_api_permit) \
.select_from(AuthResourceApiExt) \
.outerjoin(user_api_permit, AuthResourceApiExt.id == user_api_permit.api_id) \
.outerjoin(role_api_permit,
(user_api_permit.api_id == role_api_permit.api_id) & (role_api_permit.role_id == role.id)) \
.filter(AuthResourceApiExt.parent_id == self.id, user_api_permit.role_id == user.role_id) \
.all()
# 如果当前用户用的SUPER_ADMIN_TOKEN,或者用户的角色是超级管理员,走下面的逻辑
# 返回值类型为 List[Tuple[AuthResourceApiExt, AuthPermitRoleApi]]
else:
children_with_permit = db.session.query(AuthResourceApiExt, AuthPermitRoleApi) \
.select_from(AuthResourceApiExt) \
.outerjoin(AuthPermitRoleApi,
(AuthResourceApiExt.id == AuthPermitRoleApi.api_id) & (AuthPermitRoleApi.role_id == role.id)) \
.filter(AuthResourceApiExt.parent_id == self.id) \
.all()
# 先定义一个空列表,用于存储最终结果
children_detail_with_role_permit = []
# 处理上面查询获得的 children_with_permit列表套元组的数据
for child_api, child_permit in children_with_permit:
list_child_children = None
if child_api.type != 1:
# api子对象的type字段的值如果不为1,说明该子对象也是一个父对象,再递归调用自身函数
list_child_children = child_api.find_children_detail_with_role_permit(role, user)
if list_child_children:
children_detail_with_role_permit.append({
'id': child_api.id,
'title': child_api.title,
'type': child_api.type,
'description': child_api.description,
'permit': True if child_permit else False,
# 可以这样改,这样改就不需要给api_role多对多表起别名,然后连这个表两次,一个用来过滤,
# 一个用来判断permit,直接连一次过滤一下就行了
# 但这样改,想要判断要查询的角色是否有,每一个子api的权限,
# 那这个orm查询要执行好多次,效率有点低了!!!!!!!!
# 'permit': True if db.session.query(AuthPermitRoleApi).filter(role_id==role.id,api_id==child_api).first() else False,
'children': list_child_children
})
# 如果child_permit为None,说明api对象的type字段的值等于1,说明该api对象是个子对象,不再递归了!!!
else:
children_detail_with_role_permit.append({
'id': child_api.id,
'title': child_api.title,
'type': child_api.type,
'description': child_api.description,
'permit': True if child_permit else False
})
# 返回一个列表套字典的数据
return children_detail_with_role_permit
# -----------------------------------------------------------
# -----------------------------------------------------------
# 每一个能给当前用户展示的 api对象,调用该方法,获取一个字典数据
def find_detail_with_permit_and_children_and_children_permit(self, role: AuthRole, user: AuthUser = None):
# self是查出来的api对象
# 如果要查的角色对象的id为1,说明要查的角色是根管理员
if role.id == 1:
result = True
# 如果要查的角色对象的id不为1,不是根管理员
else:
# 查询 角色与api多对多表里,api_id等于当前传进来的api对象id的,并且role_id等于要查的角色id的行对象
# 如果有说明,该要查的角色也有该api对象的权限!!!
# 相反如果没查出来,说明该要查的角色没有该api对象的权限!!!
# 执行该ORM的目的就是,通过查询结果确定,要查的角色是否有该api对象的权限,据此给permit键赋值
result: AuthPermitRoleApi = db.session.query(AuthPermitRoleApi) \
.filter(AuthPermitRoleApi.api_id == self.id, AuthPermitRoleApi.role_id == role.id) \
.first()
# api对象调用find_children_detail_with_role_permit方法,并将要查的角色对象与当前用户对象作为参数传入
# 目的就是确定当前的api对象,有没有子api对象,如果没有,返回一个空列表,那么就不再递归了
# 如果有子api对象,那么就返回一个列表套字典的数据,列表套字典的数据里,每一个字典里,都包含一个子api对象的数据
list_children_dict = self.find_children_detail_with_role_permit(role, user)
# 也就是说到这一步,当前的api对象的所有数据已经确定了
# 如果有子api对象,通过children键把子对象数据 list_children_dict [{},{},{}]挂上去
# 没有子api对象,api对象的数据,就正常返回就行了!!!
if list_children_dict == []:
return {
'id': self.id,
'title': self.title,
'type': self.type,
'description': self.description,
'permit': True if result else False
}
return {
'id': self.id,
'title': self.title,
'type': self.type,
'description': self.description,
'permit': True if result else False,
'children': list_children_dict
}
# -----------------------------------------------------------
@blueprint.route('/resource_api_list_tree_with_role_permit', methods=['POST'])
@check_permit_role_api(118, SUPER_ADMIN_TOKEN, [1])
@body_need_keys(['role_id'])
@check_request_json_key_type([('role_id', int), ('api_parent_id', int)])
@token_required_with_user_allowed_pass_token(SUPER_ADMIN_TOKEN)
def resource_api_list_tree_with_role_permit(user: AuthUser):
# 当前用户所能看到的API资源树下,要查的角色id对应的API资源是否允许查看
# 先根据当前用户传过来的要查的role_id,查到对应的角色对象
role: AuthRole = db.session.query(AuthRole).where(AuthRole.id == request.json['role_id']).first()
# 判断一下有没有该角色对象
if role is None:
return jsonify_return_body(body_key_list=['code', 'msg', 'result'], result=False, msg='无此id用户角色')
# 判断一下token_required_with_user_allowed_pass_token装饰器传给,视图函数的user是不是空,是空说明,用的SUPER_ADMIN_TOKEN登录的,
# 此时只知道是超级管理员,不知道具体用户是谁, 如果user有值说明是普通的token,装饰器传给视图函数的user是该token对应的用户对象
# 此时先判断该用户的角色是否为1,也就是判断是不是超级管理员角色
if user and user.role_id != 1:
# 如果没有用SUPER_ADMIN_TOKEN,并且该用户不是超级管理员,走如下逻辑
# 如果传入了api_parent_id对应的值,查询api表里parent_id等于该值的子对象,并通过与api与角色多对多表连表,再次过滤出
# api与角色多对多表中role_id等于当前用户的role_id的所有对象,也就是通过连表与两个过滤条件,过滤出的是
# 同时满足api表中parent_id字段对应的值等于传入的要查的值,
# 并且连表的api与角色多对多表中,这些api_id对应的role_id中,必须有当前用户所属的role_id,的所有子api对象
# 这些对象就是当前用户,在parent_id对应的父组下,所能允许使用的api子对象!!!
# 当前api表里面定义的 account的id为100 biz的id为200 node的id为300
# 这3个既是根api对象的子对象,也同时是父对象,他们下面挂了很多对应的子api对象
if 'api_parent_id' in request.json.keys():
roots: List[AuthResourceApiExt] = db.session.query(AuthResourceApiExt) \
.select_from(AuthResourceApiExt) \
.outerjoin(AuthPermitRoleApi, AuthResourceApiExt.id == AuthPermitRoleApi.api_id) \
.filter(
AuthResourceApiExt.parent_id == request.json['api_parent_id'],
AuthPermitRoleApi.role_id == user.role_id
).all()
# 感觉过滤条件这样 AuthResourceApiExt.id == request.json['api_parent_id'] 更合理 找出id是要查的api_parent_id的父api对象出来
# 但是现在前端想的是,如果传入了api_parent_id对应的值,就直接过滤出该api_parent_id对应的子api对象出来,不想再展示这个父api对象的信息了
# 所以这里才用 AuthResourceApiExt.parent_id == request.json['api_parent_id'] 这个过滤条件的
else:
# 如果不传api_parent_id对应的值,则过滤出parent_id=0的api对象也就是
# account/biz/node 这3个api对象
# 并且在该基础上,继续过滤出连表的api与角色多对多表的role_id等于用户的role_id 的api对象出来
roots: List[AuthResourceApiExt] = db.session.query(AuthResourceApiExt) \
.select_from(AuthResourceApiExt) \
.outerjoin(AuthPermitRoleApi, AuthResourceApiExt.id == AuthPermitRoleApi.api_id) \
.filter(
AuthResourceApiExt.parent_id == 0,
AuthPermitRoleApi.role_id == user.role_id
).all()
# 如果当前用户用的SUPER_ADMIN_TOKEN,或者用户的角色是超级管理员,走下面的逻辑
else:
# 如果请求中有api_parent_id
# 过滤出parent_id等于对应值的所有子api对象
if 'api_parent_id' in request.json.keys():
roots: List[AuthResourceApiExt] = db.session.query(AuthResourceApiExt).filter(
AuthResourceApiExt.parent_id == request.json['api_parent_id']).all()
# 如果不传api_parent_id对应的值,则过滤出parent_id=0的所有子api对象
# 也就是 account/biz/node 这3个api对象 这3个api对象既是子对象,也是父对象
else:
roots: List[AuthResourceApiExt] = db.session.query(AuthResourceApiExt).filter(
AuthResourceApiExt.parent_id == 0).all()
# roots 得到的是 能给当前用户展示的 api对象列表 可以是父api对象的列表,也可以是子api对象的列表
# 取决于你有没有传api_parent_id对应的值,没传,我就给你parent_id等于0的子api对象 也就是account/biz/node 包含这3个父api对象的列表
# 传了,比如你传api_parent_id=0 我就给你parent_id等于0的子api对象 那也就是 account/biz/node 包含这3个父api对象的列表
# 如果你传api_parent_id=1 我就给你parent_id等于1的子api对象 那也就是包含 account父api对象下面所有的子对象的列表
# 所有该视图函数代码走到这,实际上才干了一件事,根据当前用户是不是管理员或者普通用户,获取到该用户能看到的子api对象列表来
# 前端如果不传过滤条件,api_parent_id, 那么就正常以parent_id == 0 过滤出该用户能看到的子api对象列表来
# 前端传了过滤条件,api_parent_id=xxx, 那么就正常以parent_id == xxx 过滤出该用户能看到的子api对象列表来
# 返回json格式的返回体,包括result, code, msg, data
return jsonify_return_body(
body_key_list=['result', 'code', 'msg', 'data'],
result=True,
data=[root.find_detail_with_permit_and_children_and_children_permit(role, user) for root in roots]
)
# data=[root.find_detail_with_permit_and_children_and_children_permit(role, user) for root in roots]
# for循环出roots列表里面的每一个api对象出来,每个api对象去调用find_detail_with_permit_and_children_and_children_permit方法
# 并将上面要查的角色对象与当前用户对象作为参数传入
# 也就是说data变量是一个列表套字典,每一个字典里面还有可能通过child键再挂一个列表,
# 该接口最后所达到的效果就是,如果是token是SUPER_ADMIN_TOKEN,或者用户的角色是超级管理员,data字段里面展示所有的父api对象信息,[{},{},{}]
# 并且父api对象信息的字典里面的children键里面,再挂一个列表,列表里面是父api对象的所有子api对象信息,[{},{},{}]]
# 然后所有的这些api对象信息,无论是父api对象还是子api对象,都有一个permit字段,来表示你要查的这个角色对象是否拥有访问这个api对象的权限
# 如果token不是SUPER_ADMIN_TOKEN,或者用户的角色不是超级管理员,data字段里面展示的是你拥有的访问权限的api对象信息,[{},{},{}]]
# 并且父api对象信息的字典里面的children键里面,再挂一个列表,列表里面是父api对象的所有子api对象信息,[{},{},{}]]
# 然后所有的这些api对象信息,无论是父api对象还是子api对象,都有一个permit字段,来表示你要查的这个角色对象是否拥有访问这个api对象的权限
.
.
.
.
.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY