多数据源模式JPA整合sharding-jdbc实现数据脱敏

目录
  • 前言
  • 引入依赖
  • 添加sharding数据源配置
  • 排除自动装配
  • 业务数据源配置
  • 加解密数据源配置
  • 加解密数据源的使用
  • 结语

前言

前一篇博文,透明化Sharding-JDBC数据库字段加解密方案

已经完整的介绍了数据库脱敏的场景以及方案,来自京东数科的Sharding-JDBC开源项目通过对数据源中间代理的方式透明化的实现了这个功能,但是,功能虽然实现了,sql兼容的小问题还是很多,比如目前还不支持子查询,数据库定义的关键字不允许使用,等等问题,反观我们需要加解密的字段,其实占比非常小,即使遇到了和组件不兼容的地方也可以稍加改动解决掉,所以最后博主给出了一个比较完善的组件集成方案:多数据源模式,需要加解密的数据源和业务其他数据源隔离。

既解决了数据库字段加解密的额问题,同时也解决了组件对sql的兼容问题。

下面是具体的集成步骤以及需要注意的点

引入依赖

            <dependency>
                <groupId>org.apache.shardingsphere</groupId>
                <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
                <version>${sharding.jdbc.version}</version>
            </dependency>

这里需要说明下,虽然采用多数据源兼容后,不能使用组件基于spring boot自动装配功能了,但是这里还是建议导入sharding-spring-boot-starter包,因为这个包下内置了配置映射的类,在自定义数据源的时候非常有用

添加sharding数据源配置

#数据库源配置
spring.shardingsphere.datasource.name = ds
spring.shardingsphere.datasource.ds.type = com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds.jdbc-url = jdbc:mysql://xxx?autoReconnect=true&useUnicode=true&characterEncoding=utf-8
spring.shardingsphere.datasource.ds.username = root
spring.shardingsphere.datasource.ds.password = xxx
spring.shardingsphere.encrypt.encryptors.encryptor_aes.type = aes
spring.shardingsphere.encrypt.encryptors.encryptor_aes.props.aes.key.value = 123456
spring.shardingsphere.encrypt.tables.account.columns.password.plainColumn = password
spring.shardingsphere.encrypt.tables.account.columns.password.cipherColumn = password_encrypt
spring.shardingsphere.encrypt.tables.account.columns.password.encryptor = encryptor_aes
spring.shardingsphere.props.sql.show = true
spring.shardingsphere.props.query.with.cipher.column = true

排除自动装配

@SpringBootApplication(exclude = SpringBootConfiguration.class)

由于导入了starter包,所以这里需要手动排除自动装载类,

业务数据源配置

多数据源后,业务本身的数据源也需要手动配置,默认的spring boot jpa自动转载类会判断上线文中是否存在EntityManagerFactory类,如果有就不会初始化了,所以两个数据源都需要手动配置

@Configuration
@EnableConfigurationProperties(JpaProperties.class)
public class DataSourceConfiguration{
    private final JpaProperties jpaProperties;
    private final Environment environment;

    public DataSourceConfiguration(JpaProperties jpaProperties, Environment environment) {
        this.jpaProperties = jpaProperties;
        this.environment = environment;
    }
    @Primary
    @Bean
    public DataSource dataSource(){
        String prefix = "spring.shardingsphere.datasource.";
        String each = getDataSourceNames(prefix).get(0);
        try {
            return  getDataSource(prefix, each);
        } catch (final ReflectiveOperationException ex) {
            throw new ShardingSphereException("Can't find datasource type!", ex);
        }
    }
    @Primary
    @Bean
    public EntityManagerFactory entityManagerFactory() {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setDatabase(Database.MYSQL);
        vendorAdapter.setGenerateDdl(true);
        vendorAdapter.setShowSql(true);
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
        factory.setPersistenceUnitName("default");
        factory.setPackagesToScan(Constants.BASE_PACKAGES);
        factory.setDataSource(dataSource());
        factory.setJpaPropertyMap(jpaProperties.getProperties());
        factory.afterPropertiesSet();
        return factory.getObject();
    }
    @Bean
    @Primary
    public EntityManager entityManager(EntityManagerFactory entityManagerFactory){
        return SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory);
    }
    @Primary
    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory){
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory);
        return txManager;
    }
    private ListgetDataSourceNames(final String prefix) {
        StandardEnvironment standardEnv = (StandardEnvironment) environment;
        standardEnv.setIgnoreUnresolvableNestedPlaceholders(true);
        return null == standardEnv.getProperty(prefix + "name")
                ? new InlineExpressionParser(standardEnv.getProperty(prefix + "names")).splitAndEvaluate() : Collections.singletonList(standardEnv.getProperty(prefix + "name"));
    }
    @SuppressWarnings("unchecked")
    private DataSource getDataSource(final String prefix, final String dataSourceName) throws ReflectiveOperationException {
        Map dataSourceProps = PropertyUtil.handle(environment, prefix + dataSourceName.trim(), Map.class);
        Preconditions.checkState(!dataSourceProps.isEmpty(), "Wrong datasource properties!");
        DataSource result = DataSourceUtil.getDataSource(dataSourceProps.get("type").toString(), dataSourceProps);
        DataSourcePropertiesSetterHolder.getDataSourcePropertiesSetterByType(dataSourceProps.get("type").toString()).ifPresent(
                dataSourcePropertiesSetter -> dataSourcePropertiesSetter.propertiesSet(environment, prefix, dataSourceName, result));
        return result;
    }
}

上面代码需要注意三个地方

一是数据源的配置,是以sharding的配置来解析获得的,是因为我们已经集成过了,不想改动配置,所以如此,如果还没集成过,可以直接使用spring 配置数据源的方式配置即可。

二是EntityManager的初始化,通过SharedEntityManagerCreator包装了下,是因为我们业务的查询通过继承SimpleJpaRepository来扩展功能的,通过SharedEntityManagerCreator包装保留了完整的事务功能。

三是需要给所有的业务数据源的配置添加 @Primary注解,让sprign上下文默认使用业务数据源

加解密数据源配置

/**
 * @author: kl @kailing.pub
 * @date: 2020/5/18
 */
@Configuration
@EnableConfigurationProperties({JpaProperties.class,SpringBootEncryptRuleConfigurationProperties.class, SpringBootPropertiesConfigurationProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class, DataSourceConfiguration.class})
public class EncryptDataSourceConfiguration {
    private final SpringBootPropertiesConfigurationProperties props;
    private final SpringBootEncryptRuleConfigurationProperties encryptRule;
    private final JpaProperties jpaProperties;
    private final DataSource dataSource;
    public EncryptDataSourceConfiguration(SpringBootPropertiesConfigurationProperties props, SpringBootEncryptRuleConfigurationProperties encryptRule, JpaProperties jpaProperties, DataSource dataSource) {
        this.props = props;
        this.encryptRule = encryptRule;
        this.jpaProperties = jpaProperties;
        this.dataSource = dataSource;
    }
    @Bean
    public DataSource encryptDataSource() throws SQLException {
        return EncryptDataSourceFactory.createDataSource(dataSource, new EncryptRuleConfigurationYamlSwapper().swap(encryptRule), props.getProps());
    }
    @Bean
    public EntityManagerFactory encryptEntityManagerFactory() throws SQLException {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setDatabase(Database.MYSQL);
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
        factory.setPersistenceUnitName("encryptPersistenceUnit");
        factory.setPackagesToScan(Constants.BASE_PACKAGES);
        factory.setDataSource(encryptDataSource());
        factory.setJpaPropertyMap(jpaProperties.getProperties());
        factory.afterPropertiesSet();
        return factory.getObject();
    }
    @Bean
    public EntityManager encryptEntityManager() throws SQLException {
        return SharedEntityManagerCreator.createSharedEntityManager(encryptEntityManagerFactory());
    }
    @Bean
    public PlatformTransactionManager encryptTransactionManager() throws SQLException {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(encryptEntityManagerFactory());
        return txManager;
    }
}

加解密数据源的源来自于业务数据源,只是在这里给业务数据源又代理了一层加解密的逻辑。加解密的规则配置采用了sharding-spring-boot-starter包中的映射类,所以可以保留和spring boot配置方式一致。

加解密数据源的使用

在使用时,因为默认使用的是业务数据源,所以需要在需要加解密的地方通过@Qualifier("encryptEntityManager")显示的注入加解密的数据源代理,如:

@Repository
public class AccountRepository extends AbstractJpaRepository {

    public AccountRepository(@Qualifier("encryptEntityManager") EntityManager em) {
        super(AccountModel.class, em);
    }
    @Override
    @Transactional(transactionManager = "encryptTransactionManager")
    public  S save(S entity) {
        return super.save(entity);
    }
}

另,需要手动指定加解密数据源的事务管理器

结语

没有十全十美的组件,Sharding-JDBC的数据脱敏方案已经趋向于完美了。由于组件本身的架构设计,确实不好做到100%的兼容。在发现加解密组件不支持子查询时,博主发现实现这个功能很简单,尝试过向官方添加这个功能提交pr。经过对组件的进一步了解发现,从全局考虑实现这个功能非常复杂,也就放弃了。目前这个多数据源模式可以很好的解决这个问题,如果有更好的集成方案,欢迎在下面留言交流

以上就是多数据源模式JPA整合sharding-jdbc实现数据脱敏的详细内容,更多关于多数据源JPA整合sharding-jdbc数据脱敏的资料请关注我们其它相关文章!

(0)

相关推荐

  • ShardingSphere jdbc集成多数据源的实现步骤

    目录 集成sharding jdbc 1. 引入依赖 2. 配置分表规则 问题 集成多数据源 1. 引入依赖 2. 多数据源配置 3. 增加多数据源配置 4. 使用 总结 最近有个项目的几张表,数量级在千万以上,技术栈是SpringBoot+Mybatis-plus+MySQL.如果使用单表,在进行查询操作,非常耗时,经过一番调研,决定使用分表中间件:ShardingSphere. ShardingSphere今年4月份成为了 Apache 软件基金会的顶级项目,目前支持数据分片.读写分离.多数

  • Spring Boot+Jpa多数据源配置的完整步骤

    关于 有时候,随着业务的发展,项目关联的数据来源会变得越来越复杂,使用的数据库会比较分散,这个时候就会采用多数据源的方式来获取数据.另外,多数据源也有其他好处,例如分布式数据库的读写分离,集成多种数据库等等. 下面分享我在实际项目中配置多数据源的案例.话不多说了,来一起看看详细的介绍吧 步骤 1.application.yml文件中,配置数据库源.这里primary是主库,secondary是从库. server: port: 8089 # 多数据源配置 #primary spring: pri

  • SpringBoot+Jpa项目配置双数据源的实现

    目录 引言 配置yml文件 创建数据源配置类 为每个数据库创建配置类 引言 今天为大家带来一些非常有用的实战技巧,比如在我们需要对两个数据库进行操作的时候而哦我们通常用的只是单数据库查询,这就触及到知识盲点了,那么废话不多说上代码! 配置yml文件 server: port: 8080 spring: profiles: active: dev jackson: time-zone: GMT+8 # 这里是我们的数据库配置地方 datasource: data1: #这里是数据库一 driver

  • spring+Jpa多数据源配置的方法示例

    今天临下班时遇到了一个需求,我的管理平台需要从不同的数据库中获取数据信息,这就需要进行Spring的多数据源配置,对于这种配置,第一次永远都是痛苦的,不过经历了这次的折磨,今后肯定会对这种配置印象深刻.我们这里简单回顾一下流程. 我们配置了两个数据库,一个是公司的数据库,另一个是我本地的一个数据库.首先是application.yml的配置(其中对于公司的数据库我们采取了假的地址,而本机的数据库是真是存在对应的表和库的) 数据库信息: 数据表信息: 1.application.yml datas

  • Spring Boot整合JPA使用多个数据源的方法步骤

    介绍 JPA(Java Persistence API)Java 持久化 API,是 Java 持久化的标准规范,Hibernate 是持久化规范的技术实现,而 Spring Data JPA 是在 Hibernate 基础上封装的一款框架. 第一次使用 Spring JPA 的时候,感觉这东西简直就是神器,几乎不需要写什么关于数据库访问的代码一个基本的 CURD 的功能就出来了.在这篇文章中,我们将介绍 Spring Boot 整合 JPA 使用多个数据源的方法. 开发环境: Spring B

  • SpringBoot整合sharding-jdbc实现自定义分库分表的实践

    目录 一.前言 二.简介 1.分片键 2.分片算法 三.程序实现 一.前言 SpringBoot整合sharding-jdbc实现分库分表与读写分离 本文将通过自定义算法来实现定制化的分库分表来扩展相应业务 二.简介 1.分片键 用于数据库/表拆分的关键字段 ex: 用户表根据user_id取模拆分到不同的数据库中 2.分片算法 可参考:https://shardingsphere.apache.org/document/current/cn/user-manual/shardingsphere

  • 多数据源模式JPA整合sharding-jdbc实现数据脱敏

    目录 前言 引入依赖 添加sharding数据源配置 排除自动装配 业务数据源配置 加解密数据源配置 加解密数据源的使用 结语 前言 前一篇博文,透明化Sharding-JDBC数据库字段加解密方案 已经完整的介绍了数据库脱敏的场景以及方案,来自京东数科的Sharding-JDBC开源项目通过对数据源中间代理的方式透明化的实现了这个功能,但是,功能虽然实现了,sql兼容的小问题还是很多,比如目前还不支持子查询,数据库定义的关键字不允许使用,等等问题,反观我们需要加解密的字段,其实占比非常小,即使

  • SpringBoot+Spring Data JPA整合H2数据库的示例代码

    目录 前言 Maven依赖 Conroller 实体类 Repository 数据库脚本文件 配置文件 启动项目 访问H2数据库 查看全部数据 H2数据库文件 运行方式 前言 H2数据库是一个开源的关系型数据库.H2采用java语言编写,不受平台的限制,同时支持网络版和嵌入式版本,有比较好的兼容性,支持相当标准的sql标准 提供JDBC.ODBC访问接口,提供了非常友好的基于web的数据库管理界面 官网:http://www.h2database.com/ Maven依赖 <!--jpa-->

  • spring boot使用sharding jdbc的配置方式

    本文介绍了spring boot使用sharding jdbc的配置方式,分享给大家,具体如下: 说明 要排除DataSourceAutoConfiguration,否则多数据源无法配置 @SpringBootApplication @EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) public class Application { public static void main(String[] arg

  • SpringBoot 如何使用sharding jdbc进行分库分表

    目录 基于4.0版本,Springboot2.1 在pom里确保有如下引用 里面我profiles.active了另一个 之后手工把表都建好 写个测试代码 需要注意一个坑 基于4.0版本,Springboot2.1 之前写过一篇使用sharding-jdbc进行分库分表的文章,不过当时的版本还比较早,现在已经不能用了.这一篇是基于最新版来写的. 新版已经变成了shardingsphere了,https://shardingsphere.apache.org/. 有点不同的是,这一篇,我们是采用多

  • Spring JPA整合QueryDSL的示例代码

    前言 Spring JPA是目前比较常用的ORM解决方案,但是其对于某些场景并不是特别的方便,例如查询部分字段,联表查询,子查询等. 而接下来我会介绍与JPA形成互补,同时也是与JPA兼容得很好的框架QueryDSL. 同时由于目前主流使用Spring Boot,所以本文也会基于Spring Boot来进行演示 如果对于长文无感,但是又希望了解QueryDSL可以直接查看文章最后的总结 环境信息 以下为示例的关键环境信息 JDK 1.8 maven 3.6.1 SpringBoot 2.2.0.

  • SpringBoot集成Sharding Jdbc使用复合分片的实践

    目录 1.Sharing JDBC 简介 2.系统改造 2.1 对接外部系统的系统 2.2 内部系统间的调用 3.解决方案 4.代码实现 4.1 Sharding JDBC 配置 4.2 数据源操作类 4.3 分片测试类 4.4 测试结果 参考文章: 最近主要的工作重心是数据库的容量规划. 随着业务的逐渐增大,原有保存在单表的数据量也日益增强.数据库数据会随着业务的发展而不断增多,因此数据操作,如增删改查的开销也会越来越大.再加上物理服务器的资源有限(CPU.磁盘.内存.IO 等).最终数据库所

  • 解决sharding JDBC 不支持批量导入问题

    目录 sharding JDBC 不支持批量导入 sharding-jdbc不支持多条sql语句批量更新 修改思路 sharding JDBC 不支持批量导入 package com.ydmes.service.impl.log; import com.ydmes.domain.entity.log.BarTraceBackLog; import org.springframework.beans.BeansException; import org.springframework.contex

  • SpringBoot 2.0 整合sharding-jdbc中间件实现数据分库分表

    一.水平分割 1.水平分库 1).概念:  以字段为依据,按照一定策略,将一个库中的数据拆分到多个库中. 2).结果  每个库的结构都一样:数据都不一样:  所有库的并集是全量数据: 2.水平分表 1).概念  以字段为依据,按照一定策略,将一个表中的数据拆分到多个表中. 2).结果  每个表的结构都一样:数据都不一样:  所有表的并集是全量数据: 二.Shard-jdbc 中间件 1.架构图 2.特点 1).Sharding-JDBC直接封装JDBC API,旧代码迁移成本几乎为零. 2).适

  • JavaScript实现提交模式窗口后刷新父窗口数据的方法

    本文实例讲述了JavaScript实现提交模式窗口后刷新父窗口数据的方法.分享给大家供大家参考,具体如下: 有些时间,按需求设计,一个窗口中,点击按扭,弹出模式窗口,在模式窗口中,添加完数据后(提交),关闭模式窗口,并且刷新父窗口,而恰恰模式窗口并不支持window.opener,所以无法获得父窗口,也就是说无法在关闭模式窗口后,来得到父窗口,但是可以借助showModealDialog的返回值来判断是否刷新,当调用showModealDialog时,父窗口代码会停在这一行,当弹出的模式窗口关闭

  • 使用JDBC实现数据访问对象层(DAO)代码示例

    JAVA是面向对象的语言,开发者在操作数据的时候,通常更习惯面对一个特定类型的对象,如一个用户就是一个User类的对象.DAO层需要做的,就是为上层提供充分的对象支持,让上层再也看不到具体的数据,而是一个个活生生的对象. 增加,删除,查询和修改操作是DAO需要做的最基本的4项操作.查询一般需要提供遍历查询和id查询,对于遍历查询,DAO需要提供User泛型的list对象,对于id查询则提供已经装配好数据的User对象,至于增加和修改操作,上层一般会提供一个User对象,DAO把User对象中的数

随机推荐