IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> sql 拦截 -> 正文阅读

[大数据]sql 拦截

sql 拦截 笔记

背景

之前写过 使用p6spy+springboot 做数据库操作日志审计 但是随着需求迭代越来越多.功能模块也原来越多,对sql的审计工作越来越重要.先执行后审计已经越来越不满足需求了.在此背景下.做了一个轻量级的sql拦截.

实现方式

通过 com.github.gavlyukovskiy 对sql进行拦截,在执行sql前拦截sql,做校验.
需要执行的sql,对应开发人需要提前在系统中做报备.然后专家审核通过后才可以在系统中执行.

审核主要分为3个维度

1.业务维度,执行的sql,是否满足业务的要求.关联表的查询是否合理.
2.数据库维度,sql是否使用了索引,执行的sql是否有条件约束等等.
3.权限维度.sql执行后的影响范围,每次最多可以修改的数据限制等等.

1.sql报备,可以根据自己的情况开发系统来完成.

格式化sql方法,然后保存到系统中,同时每次验证需要执行的sql也是使用此方法.

package com.example.utils;

import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor;
import com.example.config.Constants;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 *
 * @Description:  sql工具类,解析sql
 * @Author:       liuht
 * @CreateDate:   2021-08-30 17:16:25
 * @Version:      V1.0
 *
 */

@Slf4j
public class SqlUtils {

    public static String getSql(String sql){
        return parseSql(sql);
    }

    public static void initSql(String sql,String methodName){
        String parseSql = parseSql(sql);
        Constants.SQL_MAP.put(methodName,parseSql);
//        System.out.println(Constants.SQL_MAP.toString());
    }

    private static String parseSql(String sql) {

        sql = sql.replaceAll("[\r\n]", " ");
        MySqlStatementParser parser = new MySqlStatementParser(sql);
        SQLStatement sqlStatement = parser.parseStatement();

        MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();
        sqlStatement.accept(visitor);

        String optTable = visitor.getTables().entrySet().stream().map(nameTableStatEntry -> nameTableStatEntry.getValue().toString().toLowerCase() + "_opt" + "@@" + nameTableStatEntry.getKey().getName().toLowerCase() + "_table")
                .collect(Collectors.joining("@@"));

        String cols = visitor.getColumns().stream().filter(column -> {
            if (optTable.toLowerCase().startsWith("select")) {
                return column.isSelect();
            } else if (optTable.toLowerCase().startsWith("update")) {
                return column.isUpdate();
            } else {
                return true;
            }
        }).map(column -> column.toString() + "_col").collect(Collectors.joining("@@"));


        String isWhere = "noWhere";
        if (visitor.getConditions().size() > 0) {
            isWhere = "where";
        }

        List<String> limitList = Arrays.stream(sql.split(" ")).filter(str -> "limit".equals(str.toLowerCase())).collect(Collectors.toList());

        String isLimit = "noLimit";
        if (limitList.size() > 0) {
            isLimit = "limit";
        }

//        System.out.println(String.join("@@", optTable, cols, isWhere, isLimit));
        return String.join("@@", optTable, cols, isWhere, isLimit);

    }
}

sql格式化思路

sql的格式太多.这里我找了一个简单的方法来实现.通过sql解析出sql的操作类型,操作的数据库表,操作的列,是否有where,是否有limit最后生成一个sql格式化字符串.也就是我们需要审计的.

格式化之后的sql:

#sql1
select_opt@@person_table@@person.*_col@@where@@noLimit

#sql2
select_opt@@person_table@@person.id_col@@person.first_name_col@@person.last_name_col@@person.birth_date_col@@person.deleted_col@@where@@noLimit

通过上面可以看出来2个sql的区别在于第一个查询了 person.*,而第二个sql查询的具体的列
同样的方式,我们可以格式化出update sql要修改的表,修改的列,是否有where等.
insert sql要插入的表,要插入的列,是否有where等.

2.sql拦截,在引入com.github.gavlyukovskiy包后,通过实现QueryExecutionListener来实现

引入pom文件

		<!--sql拦截包-->
		<dependency>
			<groupId>com.github.gavlyukovskiy</groupId>
			<artifactId>datasource-proxy-spring-boot-starter</artifactId>
			<version>1.7.1</version>
		</dependency>

核心代码

package com.example.config;

import com.example.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import net.ttddyy.dsproxy.ExecutionInfo;
import net.ttddyy.dsproxy.QueryInfo;
import net.ttddyy.dsproxy.listener.QueryExecutionListener;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * sql拦截器,可以拦截所有sql
 */

@Slf4j
@Component
public class SqlInterceptor implements QueryExecutionListener {


    @Override
    public void beforeQuery(ExecutionInfo executionInfo, List<QueryInfo> list) {
        //TODO 这里在sql执行前进行拦截,可以做一些权限的校验
        FastThreadLocalEntity fastThreadLocalEntity =  Constants.threadLocal.get();
        for (QueryInfo queryInfo : list) {
            log.info("beforeQuery 执行sql={}",queryInfo.getQuery().replaceAll("[\\t\\n\\r]", " "));
            //报备的sql
            String sql = Constants.SQL_MAP.get(fastThreadLocalEntity.getRequestMethod()).toString();
            //当前执行的sql
            String nowSql = SqlUtils.getSql(queryInfo.getQuery().replaceAll("[\\t\\n\\r]", " "));

            if (!sql.equals(nowSql)){
                throw new CustomException(String.format("sql验证权限不对!,录入sql=%s,当前sql=%s",sql,nowSql));
            }
        }
    }

    @Override
    public void afterQuery(ExecutionInfo executionInfo, List<QueryInfo> list) {
        //TODO 这里在sql执行后进行拦截,可以做一些验证
//        for (QueryInfo queryInfo : list) {
//            log.info("afterQuery 执行sql={}",queryInfo.getQuery().replaceAll("[\\t\\n\\r]", " "));
//        }
    }
}

流程图

流程图

git地址

https://gitee.com/mr-liu-163/sqltest.git

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-09-15 02:04:35  更:2022-09-15 02:06:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 10:54:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码