首页 > 编程语言 >python-flask 技能点使用-03 请求钩子实现审计日志

python-flask 技能点使用-03 请求钩子实现审计日志

时间:2023-05-15 11:24:48浏览次数:40  
标签:03 python request req db flask url time data

  • 场景分析

         使用python flask开发web系统,该系统是基于用户认证鉴权的web系统, 系统中涉及到关键数据的操作,因此需要针对业务操作进行记录(也就是审计日志),便于管理员后期查看,在基于java的Spring系列框架中我们可以借助于AOP面向切面的编程来完成,在使用Flask时可以借助于请求钩子  @app.before_request +  @app.after_request 来实现,关键难点在于如何保证记录的是同一个请求URL(一个是请求前预记录,一个是请求后根据返回码进行补充)

  • 代码实现
    •  如何实现预记录+修改补充,在预记录时生成一个唯一性UUID标记本次URL请求
      • 尝试一: 使用request.headers中扩充携带UUID,失败,报错说headers是不可变的
      • 尝试二: 使用request.session中扩充携带UUID,与上面类似
      • 尝试三: 通过HashMap携带,在后期分布式应用中不一定可靠
      • 尝试四: 通过请参数中扩展,存在的问题 如何在PUT、POST、GET、DELETE不同请求方式中实现
    • 定义数据库实体类
# 业务审计日志表
class BusinessAuditLog(db.Model):
    __tablename__ = 'business_aduit_record'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    uuid = db.Column(db.String(50))
    url = db.Column(db.String(50))
    req_params = db.Column(db.String(5000))
    req_method = db.Column(db.String(10))
    is_succ = db.Column(db.String(10))
    cost_time = db.Column(db.Integer)
    resp_code = db.Column(db.String(10))
    resp_data = db.Column(db.Text(5000))
    resp_msg = db.Column(db.String(500))
    user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
    start_time = db.Column(db.DateTime(50), default=datetime.now)
    end_time = db.Column(db.DateTime(50), default=datetime.now)


    def __int__(self, url, req_params, req_method, user_id, start_time, uuid):
        self.url = url
        self.req_params = req_params
        self.req_method = req_method
        self.user_id = user_id
        self.start_time = start_time
        self.uuid = uuid
    •  请求前预记录(携带token校验代码)
# 不需要验证的接口地址
no_use_auth_urls = ["/admin/user/logout", "/admin/user/login", "/admin/user/register", "/admin/user/code",
                    "/admin/menu/import", "/admin/menu/tree", "/admin/menu/tree/i18n", "/favicon.ico"]


# 在每一次请求之前调用,这时候已经有了请求,可以在这个方法里面做请求的校验
# 如果请求的校验不成功,可以直接在此方法中进行响应,直接return之后那么就不会执行视图函数了 目前必须在这个文件中才会执行
@app.before_request
def before_request():
    url = request.url
    base_url = request.base_url
    root_url = request.root_url
    host_url = request.host_url
    full_path = request.full_path
    f_path = full_path.split("?")
    bus_url = str(f_path[0])
    print("====================== ", bus_url)
    is_static = bus_url.startswith("/static")
    if bus_url not in no_use_auth_urls and is_static == False:
        print('before_request bus_url : ', bus_url)
        token = request.headers.get('Authorization')
        user_id = UserToken.get_user_id_from_token(token)
        if token is None:
            return JsonResponse.fail(9999, 'request headers missing Authorization')
        if not RedisUtil.validate_token(token):
            return JsonResponse.fail(9401,
                                     'request headers missing Authorization or Authorization is expired,need to login')
        # 对于系统相关业务的GET请求不记录
        method = request.method
        if bus_url.startswith("/admin/") and method == 'GET':
            return None
        if is_static:
            return None
        req_uuid = str(uuid.uuid4())
        req_data = str(request.data, encoding='utf-8')
        start_time = time.time()
        start_time_hm = int(round(start_time * 1000))
     # GET/DELETE请求方式中存在的情况 if req_data is None or CommonTool.is_none(req_data) or req_data == '{}': req_data = "{\"REQ_UUID\": \"" + req_uuid + "\",\"START_TIME\": " + str(start_time_hm) + "}" else: req_data_dict = json.loads(req_data) req_data_dict['REQ_UUID'] = req_uuid req_data_dict['START_TIME'] = str(start_time_hm) req_data = json.dumps(req_data_dict) req_data_b = req_data.encode(encoding='utf-8') request.data = req_data_b req_params = None if method == 'DELETE' or method == 'GET': req_params = request.args.to_dict().__str__() else: req_params = request.get_data(as_text=True) db.session.add(BusinessAuditLog(url=bus_url, req_params=req_params, req_method=method, user_id=user_id, start_time=datetime.now(), uuid=req_uuid)) db.session.commit() return None
    • 请求后补充更新 
# 在执行完视图函数之后调用,并且会把视图函数生成的响应传入,故其函数需要有一个response参数,可以在此方法中对响应进行进一步处理
@app.after_request
def after_request(response):
    full_path = request.full_path
    f_path = full_path.split("?")
    bus_url = str(f_path[0])
    is_static = bus_url.startswith("/static")
    if is_static or bus_url in no_use_auth_urls:
        return response

    response.headers['Content-Type'] = 'application/json'
    res = response.json
    resp_code = int(res['code'])
    req_data = request.data

    if resp_code != 9401 and CommonTool.is_not_none(req_data) and req_data is not None:
        resp_msg = res['msg']
        resp_data = res['data']

        req_data = str(request.data, encoding='utf-8')
        req_data_json = json.loads(req_data)
        req_uuid = req_data_json['REQ_UUID']
        start_time_hm = req_data_json['START_TIME']

        end_time = time.time()
        end_time_hm = int(round(end_time * 1000))

        method = request.method
        cost_time = end_time_hm - int(start_time_hm)
        is_succ = None
        if resp_code == 200:
            is_succ = "Y"
        else:
            is_succ = "N"
        BusinessAuditLog.query.filter(BusinessAuditLog.uuid == req_uuid,
                                      BusinessAuditLog.req_method == method,
                                      BusinessAuditLog.url == bus_url
                                      ).update(
            {
                "end_time": datetime.now(),
                "cost_time": cost_time,
                "resp_code": resp_code,
                "resp_data": json.dumps(resp_data),
                "resp_msg": resp_msg,
                "is_succ": is_succ
            }
        )
        db.session.commit()
    return response

  

标签:03,python,request,req,db,flask,url,time,data
From: https://www.cnblogs.com/dduo/p/17401063.html

相关文章

  • python的dataframe通过query使用dict字典查询
    示例```params={"坐席姓名":"唐红成"}query_string='and'.join(  [f'({key}=="{val}")'iftype(val)==strelsef'({key}=={val})'forkey,valinparams.items()])df.query(query_string)```......
  • python-flask 技能点使用-01 请求钩子
    场景分析     熟悉java开发的小伙伴应该了解Spring全生命周期以及配套的一系列方法,熟悉Vue开发的小伙伴们应该也熟悉Vue生命周期管理以及一系列方法,使用过Servlet的小伙伴也应该了解其生命周期的概念,本人之前一直从事java开发,现在因为业务需要需要学习python开发,目前......
  • 如何有技巧地运用Python 语言?
    1.三元运算符 三元运算符是if-else语句的简写。语法是value_if_trueifconditionelsevalue_if_false。三元运算符是一行代码,可以替代多行if-else语句,使你的代码更加简洁。 a=5 b=10 max=aifa>belseb #value_if_trueifconditionelsevalue_if_fa......
  • Python爬虫代理使用完整代码
    使用代理可以带来以下好处:1.隐藏真实IP地址:使用代理可以隐藏你的真实IP地址,从而保护你的隐私和安全。2.防止封禁:有些网站会限制同一IP地址的访问频率,使用代理可以避免被封禁。3.提高访问速度:使用代理可以让你的请求从代理服务器发出,从而减轻本地网络负担,提高访问速度。4.......
  • flask(六)---flask上传文件
    文件上传是很常见的功能,但这过程中却有很多技术环节需要学习文件类型,大小限制多文件上传文件名称安全检查1.文件大小限制出于资源考虑,不能不对用户上传的文件大小进行限制,这个在flask中实现非常简单fromflaskimportFlask,Requestapp=Flask(__name__)app.config[......
  • Python中字典的用法
    字典用于存放具有映射关系的数据。相当于保存了两组数据,其中一组数据是关键数据,被称为key;另一组数据可通过key来访问,被称为value。字典相当于2字段表格,但value值可通过列表的形式扩展data={"李太白”:[23742364782642342323234,28,"男","青莲居士","唐朝"],"姜子牙......
  • Python垃圾回收机制
    什么是垃圾回收机制:垃圾回收机制(简称GC)是Python解释器自带一种机制,专门用来回收不可用的变量值所占用的内存空间为什么要用垃圾回收机制:程序运行过程中会申请大量的内存空间,而对于一些无用的内存空间如果不及时清理的话会导致内存使用殆尽(内存溢出),导致程序崩溃,因此管理内存是一......
  • 2月28日学习总结20230321
    上午智慧物业管理系统Java开发有一个三层规范(包结构)controllerfileController:文件的上传的与删除service(重点)dao持久层domain:实体类的包,与数据库中的表建立映射关系,操作实体类就是操作数据库中的表。面向对象的思想,OIMcommon:工具的类,公共的组件(不再会有任何编辑,就直接使用了)messa......
  • 2月27日学习总结20230321
    上午PDDTDDDDDTDD测试驱动开发,先写模块测试,在进行模块开发;测试失败后编写实现代码,测试成功后接着迭代下一个功能DDD领域驱动设计,更多关注业务层,定义几个模块,写模块的功能BDD行为驱动开发,从用户需求出发,强调系统行为,是TDD的补充mock服务完整介绍:​​​测试开发工程必备技能之......
  • 3月7日20230321
    计划[]crm项目基本看完[]看crapapi代码[]补所不会的内容执行09点51分 学习crm知识记录springMVC项目搭建开发环境idea有项目和工程的概念,一个项目可以有多个工程创建好了项目newproject——>创建工程newmodel补全目录结构添加依赖,看用什么技术配置文件mybatis-config.xmla......