
我做了几年数据中间件和数据库治理相关的工作ShardingJDBC 用得不算少。很多人对它的印象还停留在“分库分表”上但它的数据加密功能其实同样值得专门拿出来聊一聊。尤其是前面看到“透明数据加密 TDE 白皮书”上了热搜更让我觉得应该写一篇真正能落地的文章——不聊概念只讲怎么配置、怎么踩坑、怎么在存量系统里平滑地把敏感字段加密起来。这篇文章的目标很明确围绕 ShardingJDBC 的加密模块把原理、配置、实战、迁移、排查一次讲透。适合正在做数据安全改造、等保合规、敏感信息保护的后端开发、DBA 和架构师阅读。就算你现在只是听说这个功能看完也能直接上手验证。1. 整体设计思路为什么用 ShardingJDBC 做数据加密1.1 加密需求从哪来不只是为了合规这几年“数据安全”不再只是安全部门 PPT 里的概念。身份证号、手机号、银行卡号、地址、登录密码、支付密码只要这些字段泄露出去轻则用户投诉重则监管处罚。很多公司做安全改造的第一件事就是把这些敏感字段在数据库里加密存储。但加密这件事听起来简单做起来很麻烦。项目里最常用的方式是“应用层手动加密”在 Service 里调一个encrypt()方法把密文写到数据库查询的时候再反向解密。这个方案最大的问题不是不能做而是“漏”。一个订单系统里手机号可能在用户表、订单表、物流表、日志表里出现六七次但凡漏掉一个写入点明文就落库了。更麻烦的是新同事接手以后不一定记得这个字段是加密的随手一个INSERT就把明文写进去了安全改造直接白做。所以我们需要一个“透明”的加密方案——对应用开发者来说代码里操作的是明文数据库里存的是密文读写转换全由底层框架完成。这样既不需要改动业务代码又能统一收口加密逻辑避免某个写入点漏掉。ShardingJDBC 的加密模块做的正是这件事。1.2 为什么选 ShardingJDBC 而不是数据库 TDE先说说数据库层的 TDETransparent Data Encryption。ORACLE、SQL Server、MySQL企业版、PostgreSQL 都有类似能力原理是数据库在把数据页写入磁盘之前做加密读取时自动解密。它的核心价值是防止“物理泄露”——比如数据库文件被拷走、备份文件被盗没有密钥就看不了数据。但 TDE 有一个很关键的限制它对应用层用户是透明的对数据库内部的查询引擎同样“透明”。也就是说如果攻击者能够登录数据库执行查询TDE 解完密之后返回的数据依然还是明文像 MyBatis 打印 SQL 日志、慢查询日志、binlog 同步到从库或大数据平台这些环节里数据仍然是明文。对于“防止数据库账号泄露导致拖库”这个场景TDE 其实是挡不住的。而 ShardingJDBC 的数据加密发生在客户端 Driver 层处在应用和数据库之间。应用把一条带明文的 SQL 发出去ShardingJDBC 拦截下来把明文替换成密文再发给数据库查询返回时再把密文解密成明文返回给应用。这个机制决定了它有四个实打实的好处应用层完全无感业务代码不用改密文只出现在数据库存储和网络传输链路里只要不开 MySQL 通用日志可灵活指定字段级加密不需要对整个库或整张表加密天然适配多云、多数据库环境不绑定某个数据库品牌的企业版功能当然它也不是万能的。后面会讲到 LIKE 模糊查询基本用不了密文列无法建普通索引等限制。选型时一定要结合自己的业务场景不能盲目上。1.3 ShardingJDBC 在项目里的角色定位ShardingJDBC 本质上是一个增强版 JDBC Driver它做的事情是在 JDBC 规范之上做了一层 SQL 解析、改写、路由和结果集处理。分库分表是它最常用的能力加密模块只是它众多功能中的一个。但这两个功能可以同时用比如订单表按用户 ID 分表同时手机号字段加密存储它们互不冲突因为在 SQL 改写阶段先做路由改写再做加密改写优先级是可控的。从架构位置上看它处于应用和数据库之间的“中间层”因此它既能做分库分表也能做数据脱敏、数据加密、读写分离。这也是为什么我们这次做加密改造能直接复用项目里已有的 ShardingJDBC 依赖不需要额外引入一套独立的加密中间件更不需要改数据库连接方式。2. 数据加密的核心机制与配置要点2.1 加密列、明文列与密文列的关系ShardingJDBC 的数据加密设计很有意思它把一列数据拆成两种情况来处理。先说配置层面最核心的三个概念逻辑列应用 SQL 里实际写的列名业务代码只跟它打交道明文列真实存储明文的列通常在配置里显式声明也可以不建密文列真实存储密文的列对应数据库表里实际存在的字段举个例子user表里有一个phone字段需要加密。正常情况下应用 SQL 写的是INSERT INTO user(phone) VALUES(13800138000)。配置 ShardingJDBC 加密后它会自动把这个 SQL 改写成INSERT INTO user(phone_cipher) VALUES(U2FsdGVkX1/...)把明文塞进 phone_cipher 列而 phone 列根本不用建。如果系统处于“存量数据迁移过渡期”可以让phone列保留为明文列同时建phone_cipher密文列。这样在数据迁移完成之前老数据能正常读出来新写入的数据加密落库。等迁移完成再把明文列去掉。这种设计在实战中非常实用因为它解决了加密上线过程中最头疼的“平滑过渡”问题。配置文件的写法一般是这样的rules: - !ENCRYPT tables: user: columns: phone: cipherColumn: phone_cipher encryptorName: phone_encryptor assistedQueryColumn: phone_assisted likeQueryColumn: phone_like queryWithCipherColumn: true encryptors: phone_encryptor: type: AES props: aes-key-value: 123456abc注意这里的assistedQueryColumn和likeQueryColumn它们分别对应辅助查询列和模糊查询列。基础版本的 ShardingJDBC 只支持等值查询要支持 LIKE 查询就得配 assist 列和 like 列。这块后面细讲。2.2 加密算法选择MD5、AES、RC4 各自的适用场景ShardingJDBC 内置了几种加密算法可以在encryptors里通过type指定算法类型是否可逆典型场景注意事项MD5不可逆密码校验、token 哈希无法还原原文一般不做用户敏感信息存储AES可逆手机号、身份证、银行卡号等需要回显的字段需要配置密钥支持 CBC/ECB 等模式RC4可逆简单场景速度极快安全性偏弱新项目不推荐实际项目里手机号、身份证这类字段要展示给用户必须支持解密回原文所以选择 AES。而登录密码这类字段根本不需要回显只做校验那直接用 MD5 或 BCrypt 更合理。这里要提醒一句如果用 MD5 做“加密”撞库风险非常高弱口令用彩虹表一撞就出来了建议加盐。ShardingJDBC 官方文档里也说 MD5 只适合做摘要不适合做加密存储。如果你用的是自定义加密算法可以实现 ShardingSphere 的Encryptor接口然后在配置里指定type为自定义类的全限定名。这种方式适合对接公司统一的加解密服务比如调用 KMS 获取密钥、通过加密机做加解密。2.3 查询改写原理等值查询、范围查询与模糊查询的边界理解 ShardingJDBC 加密查询的改写原理能帮你避开很多隐形坑。它处理 SQL 的方式是这样的对于WHERE phone ?它会把等值条件的参数用加密算法处理成密文再去数据库里匹配对于WHERE name LIKE %张%如果没配 like 列它没法直接对密文做模糊匹配只能报错或返回空数据对于WHERE create_time BETWEEN ? AND ?加密列不参与这种范围查询因为密文的顺序和明文顺序完全不一致核心原理是加密后的密文是一段随机字节序列AES 下它的字典序不代表明文的字典序。所以任何依赖“排序”和“匹配一部分”的操作比如BETWEEN、ORDER BY、LIKE在纯密文列上都行不通。等值查询没问题是因为 ShardingJDBC 能拿到原始明文用同一个密钥和算法加密后去匹配。数据库层面看到的是一个普通的等值条件索引也能正常使用。那遇到 LIKE 需求怎么办我的建议是“能避开就避开”。比如手机号模糊查询实际业务里更多是查“尾号 8000 的人”这种需求你可以在应用层先把所有手机号解密出来再过滤但数据量大就炸了。或者采用“数据库中额外冗余一个可模糊检索的列”用不可逆但支持匹配的方式存储比如对手机号做定长分段哈希再对这个哈希列建索引。ShardingJDBC 从 5.x 开始也支持了 like 查询列但实际上它对性能有开销后面实战部分会讲。2.4 密文列与查询结果的映射机制很多第一次用 ShardingJDBC 加密模块的人会困惑一个问题为什么我查出来的数据是明文我在数据库客户端里明明看到的是密文。这是因为 ShardingJDBC 在拿到数据库返回的结果集后会做一次反向解密处理。queryWithCipherColumn这个配置项就是开关置为true时查询结果自动解密成明文返回给应用置为false时返回数据库原始密文。默认值在不同版本不太一样5.x 开始默认是 true但在做数据迁移校验时可以临时把它改成 false直接查密文做对账。这里有个很重要的认知只要应用连接的是 ShardingJDBC 的数据源它返回的始终是解密后的明文默认配置下而数据库里存的是密文。所以对应用来说这个加密完全是透明的。但这也意味着如果项目里有多个应用直连同一个数据库比如报表系统、大数据同步任务它们拿到的可是密文。这时候要么改造这些应用也用 ShardingJDBC 数据源要么由数据侧统一处理解密提前要做好架构沟通。3. 实战从零配置 ShardingJDBC 数据加密3.1 环境与依赖准备我用 Spring Boot 2.7 ShardingJDBC 5.1.0 版本做演示MySQL 8.0JDK 1.8。先把依赖加进pom.xmldependency groupIdorg.apache.shardingsphere/groupId artifactIdshardingsphere-jdbc-core-spring-boot-starter/artifactId version5.1.0/version /dependency如果项目中原本就有 ShardingJDBC 依赖直接复用即可。担心版本冲突的话可以用shardingsphere-jdbc-core不带 Spring Boot Starter自己手动构建数据源。建表演示CREATE TABLE t_user ( id bigint(20) NOT NULL AUTO_INCREMENT, user_name varchar(64) DEFAULT NULL, phone_cipher varchar(255) DEFAULT NULL COMMENT 手机号密文, phone_assisted varchar(64) DEFAULT NULL COMMENT 辅助查询列, phone_like varchar(255) DEFAULT NULL COMMENT 模糊查询列, id_card_cipher varchar(255) DEFAULT NULL COMMENT 身份证密文, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这块有一个容易踩坑的点如果你给 phone 同时配置了明文列和密文列那数据库表里要同时有phone明文 和phone_cipher密文两个物理列而逻辑列名phone只存在于 SQL 里。3.2 Spring Boot 配置文件完整版application.yml的完整配置如下spring: shardingsphere: datasource: names: ds ds: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://localhost:3306/test_db?useSSLfalseserverTimezoneAsia/Shanghai username: root password: root123456 rules: encrypt: tables: t_user: columns: phone: cipherColumn: phone_cipher assistedQueryColumn: phone_assisted likeQueryColumn: phone_like encryptorName: aes_encryptor assistedQueryEncryptorName: assisted_encryptor likeQueryEncryptorName: like_encryptor id_card: cipherColumn: id_card_cipher encryptorName: aes_encryptor queryWithCipherColumn: true encryptors: aes_encryptor: type: AES props: aes-key-value: 123456abc assisted_encryptor: type: AES props: aes-key-value: 123456abc like_encryptor: type: CHAR_DIGEST_LIKE props: digest-algorithm-name: MD5 digest-salt: 123456 digest-salt-enabled: true props: sql-show: true注意几个关键点assistedQueryEncryptorName是辅助查询加密器的名字。ShardingJDBC 的辅助查询列不是直接存原始密文它会用另一个加密算法来生成主要是为了支持等值查询时快速定位密文行。这里我用的是 AES生成的辅助列是一个可逆的值不会影响性能。如果你的场景只需要等值查询辅助列和模糊查询列可以不用配。like_encryptor我用的是内置的CHAR_DIGEST_LIKE它会对原文做摘要处理并保存摘要值实现模糊匹配。这个算法在 5.1.0 版本里已经内置核心思路是对每个字符生成一个摘要查询时把 LIKE 条件的每个字符也做同样的摘要然后通过 SQL 改写把 LIKE 变成对摘要列的模糊匹配。官方测试是可以支持的但实际用起来会有一些限制条件我后面细说。3.3 代码层面业务无感 CRUD写一个最简单的 ServiceService public class UserService { Resource private JdbcTemplate jdbcTemplate; public void addUser(String name, String phone, String idCard) { jdbcTemplate.update(INSERT INTO t_user(user_name, phone, id_card) VALUES(?, ?, ?), name, phone, idCard); } public MapString, Object getByPhone(String phone) { ListMapString, Object list jdbcTemplate.queryForList( SELECT * FROM t_user WHERE phone ?, phone); return list.isEmpty() ? null : list.get(0); } }你看代码里完全没有加密相关的逻辑。写入的时候写的是明文phoneShardingJDBC 会自动把 SQL 改写成INSERT INTO t_user(user_name, phone_cipher, phone_assisted, phone_like, id_card_cipher) VALUES(?, ?, ?, ?, ?)参数也自动加密替换。查询的时候WHERE phone ?会被改写成WHERE phone_assisted ?并传入加密后的参数返回结果集时再把密文解密成明文。这就是它叫“透明加密”的原因业务代码零改动加解密全部由数据源层代理完成。3.4 辅助查询列和模糊查询列到底怎么选实战中很多团队只要求手机号能等值查询那配置只需要cipherColumn和encryptorName就够了。但现实业务里后台管理页面基本都有“按手机号模糊搜索”的需求这时候你必须决定要不要引入辅助查询列和模糊查询列。在解释配置之前我要先说清楚 ShardingJDBC 目前对模糊查询的实现限制辅助查询列assistedQueryColumn解决的是“等值查询但密文随机化导致匹配不到”的问题比如同一个手机号两次加密的密文不同如果直接对密文列做等值查询结果可能匹配不到。通过辅助查询列ShardingJDBC 能快速定位对应行。模糊查询列likeQueryColumn解决的是 LIKE 查询需求需要用特殊算法让密文保持部分可比较性。实际部署时模糊查询列的存储开销不小而且CHAR_DIGEST_LIKE目前对中文、特殊字符的支持要严格测试。如果你只是做简单的“按手机号后四位查询”我的建议很直接别硬上模糊查询列直接冗余一个明文脱敏列用于检索。比如冗余phone_tail字段存手机号后四位建普通索引业务查询时直接WHERE phone_tail 8000又快又稳。如果一定要用 ShardingJDBC 的模糊查询列那你得知道它存在两个局限一是只支持尾部模糊xxx%和包含模糊%xxx%的部分场景二是性能上因为要对查询条件做摘要处理SQL 改写后可能变成WHERE phone_like LIKE %hash1%hash2%索引基本失效。所以生产环境使用前一定要在真实数据量下压测。3.5 存量数据加密迁移方案做加密改造最怕的就是线上已有几百万存量数据不能直接清掉重来。ShardingJDBC 针对这种情况提供了明文列和密文列同时存在的过渡方案。具体操作分三步表结构同时保留明文列和密文列配置里声明plainColumn和cipherColumn先把存量数据同步到密文列一次性加密完成验证数据一致性后去掉明文列从配置里也删掉plainColumn配置写法举例如下columns: phone: plainColumn: phone cipherColumn: phone_cipher encryptorName: aes_encryptor保存旧数据的时候应用 INSERT 的 SQL 里带了明文和密文两个写入目标吗其实不需要ShardingJDBC 会自动把逻辑列的明文同时更新到明文列和密文列。也就是说过渡期新写入的数据两边都是正确的老的明文数据靠一次性脚本补齐。迁移脚本可以参考UPDATE t_user SET phone_cipher AES_ENCRYPT(phone, 123456abc) WHERE phone_cipher IS NULL;这是 MySQL 函数写法但如果你用的加密算法不是 MySQL 内置 AES 的格式会跟 ShardingJDBC 的密文格式不一致解密会失败。更可靠的方式是写一个一次性 Java 迁移程序通过 ShardingJDBC 数据源把明文读取出来再更新回去让 ShardingJDBC 自己完成密文列的维护。我实际踩过这个坑所以这里强烈不建议直接写 SQL 迁移。4. 常见问题与排查技巧实录4.1 密文列的值解不出来密文格式不一致排查加密问题第一件事就是看数据库里存的密文是不是以U2FsdGVkX1开头。这不是乱码而是 ShardingJDBC 的 AES 加密结果经过 Base64 编码后的固定前缀。如果你看到数据库里存的是一长串和代码里encrypt()方法手动加密完全不同的内容不用慌这是正常现象。出现解不出来的情况绝大多数是以下几个原因修改了aes-key-value配置密钥对不上数据迁移时用 SQL 函数直接更新了密文列密文格式和 ShardingJDBC 不一致配置里encryptorName指向了错误的加密器不同环境用了不同的加密算法 type排查命令很简单先直连数据库看一眼密文列的值再用 ShardingJDBC 数据源写一个查询接口对比两者。如果数据库里是明文、逻辑查询返回也是明文那说明加密配置没生效——大概率是 Spring Boot 自动装配时没有加载 ShardingJDBC 数据源或者rules配置写错地方了。还有一个我见过很多次的场景项目里同时引入了 MyBatis-Plus 和 ShardingJDBC 的 Starter导致数据源被 MyBatis-Plus 覆盖。这时候你的数据源不是 ShardingJDBC 代理的数据源所有加密规则自然不生效。解决办法是手动注入 ShardingDataSource或者在配置里确认 MyBatis-Plus 的mapper-locations扫描了正确的MapperScan。建议每次上线加密改造前先跑一条测试数据然后直连数据库看密文再通过应用查询看明文三步确认链路是通的。4.2 LIKE 查询返回空数据或直接报错前面说了ShardingJDBC 的模糊查询列目前不是所有场景都能兜住。我在实际项目中就遇到过一个问题配置了likeQueryColumn和CHAR_DIGEST_LIKE查询WHERE phone LIKE 138%能返回结果但是查询WHERE phone LIKE %8000直接返回空。排查过程不复杂打开sql-show: true看 ShardingJDBC 最终发给数据库的 SQLSELECT * FROM t_user WHERE phone_like LIKE %3f2a%你会发现它把查询条件里的%8000转换成了对摘要列的模糊匹配这就要求数据库里 phone_like 列存的是“分段摘要”而这块依赖底层算法实现如果算法不支持某种长度或位置的模糊结果就是空。我的建议是如果确实需要做手机号模糊搜索不要纠结于 ShardingJDBC 的 like 能力。在表里额外加一个明文冗余列比如phone_search专门用于后台搜索同时用 ShardingJDBC 的密文列存储真正的敏感数据。这不是退而求其次而是为了在“响应速度和查询灵活性”之间找到平衡。安全合规要求的核心是敏感字段不以明文落库一个专门用于模糊搜索且脱敏后的字段并不违背这一原则。4.3 加了加密后写入性能明显下降加密模块对写入性能的影响主要来自两部分SQL 解析和 SQL 改写的开销以及加密算法本身的计算开销。SQL 解析在 ShardingJDBC 任何功能里都存在一般影响很小。加密算法 AES 的计算开销也不大单条数据微秒级。但如果你的表里同时配置了辅助查询列和模糊查询列意味着每次 INSERT 不仅要加密原文还要生成辅助列值和摘要列值写放大是三倍。像手机号这种字段一次写入从原来的一列变成三列存储空间也会明显上升。实测下来单表写入性能下降大概在 5%~10% 之间这在绝大多数业务场景下是可以接受的。但如果你的系统是高频写入的流水型业务比如风控埋点、日志采集建议只对必要的字段做加密且尽量不加辅助列和模糊列。批量插入场景有个技巧JDBC 的rewriteBatchedStatementstrue对 ShardingJDBC 加密也有正向作用能明显减少网络往返。MySQL 连接串上加这个参数即可。4.4 加密字段和分库分表同时使用时的注意事项既然项目里已经在用 ShardingJDBC很可能同时开了分库分表。这种情况下要特别注意分片键和加密列的关联。分片的计算通常发生在 SQL 改写阶段的前半段ShardingJDBC 会先拿到分片字段的值来决定路由到哪个库哪张表。如果分片键也是一个加密字段逻辑上就可能出问题分片值用的是应用传入的明文比如手机号但实际数据库表里根本没有存储明文那从数据库端反向查询时就不知道应该路由到哪张表。更严重的场景是如果分片键是手机号加密后手机号变成一长串密文哈希分布和原手机号的分布完全不同这会导致历史数据落到错误的分片。所以我的建议非常明确用非敏感字段作为分片键比如用户 ID、订单 ID。敏感字段做加密列使用可以但不要让它参与到分库分表的路由计算中。这个坑在改造存量分库分表系统时尤其容易踩因为原来的分片键可能就是手机号一改加密逻辑整个路由全乱了。如果实在避免不了需要提前在配置里显式指定分片算法使用的列是逻辑列并做好数据迁移的完整预案这个复杂度会成倍上升不太建议在初版加密改造中同时处理。4.5 常见问题速查表问题现象可能原因排查方向数据库存了明文加密没生效数据源未被 ShardingJDBC 代理检查 Starter 依赖、数据源配置、MapperScan查询结果出现乱码密钥不一致或密文格式错误对比数据库密文和配置密钥检查迁移脚本LIKE 查询返回空未配置 likeQueryColumn 或算法不支持打开 sql-show 看改写后的 SQL考虑冗余脱敏列写入性能劣化明显辅助列、模糊列过多精简加密列关闭 sql-show分库分表数据路由错乱分片键是加密列改用非敏感字段作为分片键加密列无法建索引密文随机化导致索引无意义等值查询建辅助列索引LIKE 用冗余列新写入的数据数据库里能看到明文plainColumn配置未删除过渡期结束后及时移除明文列5. 透明数据加密TDE与 ShardingJDBC 加密的对比既然热搜里提到了“透明数据加密 TDE 白皮书”这里多聊几句。TDE 是数据库厂商提供的能力比如 MySQL 企业版、Oracle TDE、SQL Server TDE。它和 ShardingJDBC 的数据加密定位完全不同很多人会把它们搞混。TDE 的核心是“数据文件加密”数据库在把 Buffer Pool 中的数据页刷到磁盘之前进行加密读回内存时自动解密。这种方案的价值在于防止数据库文件被盗后直接被读取对应用和 SQL 完全不感知也不需要改表结构。但它解决不了“通过数据库账号查询获得明文数据”的问题因为数据库正常运行状态下TDE 对 SQL 层是透明的。ShardingJDBC 加密则是在 SQL 执行链路里做字段级处理落库的就是密文。这种情况下即使数据库账号泄露攻击者通过 SQL 查到的也是密文除非他同时拿到应用服务器上的密钥。所以从“防拖库”的角度看ShardingJDBC 加密的强度反而更高。但这不是说 TDE 没用它们是不同纵深防御层次的东西TDE 防硬盘被盗、备份文件泄露ShardingJDBC 防数据库账号泄露、应用日志泄露、binlog 泄露权限管控负责限制谁能用数据库账号审计负责发现异常查询行为比较理想的做法是关键库开启 TDE如果数据库支持敏感字段同时用 ShardingJDBC 加密双管齐下。当然代价是性能开销和运维复杂度都要翻倍是否同时上要看系统的安全等级和预算。6. 加密密钥管理一个容易被忽视的实战细节很多文章讲 ShardingJDBC 加密要么只贴配置要么只跑 CRUD很少有人认真讲密钥怎么管。但根据我的经验密钥管理才是上线后最可能出事故的地方。配置里写死aes-key-value只是最简单的 demo 做法。生产环境如果也这么干一旦代码仓库泄露密钥跟着泄露整个加密体系形同虚设。更关键的是如果你的密钥写死在配置中心某天运维不小心改了配置所有已加密的数据都会解不出来比删库还可怕。我的建议分三层测试环境可以用配置里的aes-key-value直配方便调试预发环境从环境变量或 KMS 动态获取密钥配置里只留密钥引用生产环境必须使用公司内部的密钥管理系统KMS或自建的加密服务ShardingJDBC 支持自定义加密器可以对接 KMS 的 API。这样即使密文泄露没有 KMS 的权限也解不开另外强烈建议做密钥轮换演练。很多人觉得 AES 密钥轮换简单改个配置重启就行。事实是如果密钥变了数据库里已有的所有密文都靠旧密钥加密重启后所有查询都会解密失败。合理的做法是采用“双密钥”新数据用新密钥加密旧数据在查询时先用新密钥解失败后用旧密钥兜底然后逐步迁移数据。ShardingJDBC 官方没有直接提供这个能力但你可以通过自定义 Encryptor 实现这是高阶玩法建议有精力再搞。7. 从实战角度给几个选型建议最后给还没动手的团队几个建议全是个人经验第一不要试图一次性把所有敏感字段都加密。选业务价值最高、泄露影响最大的字段先做比如手机号、身份证号跑通全流程后再逐步扩展。一次改太多字段排查问题的时候根本分不清是哪个环节出了问题。第二加密上线前一定要做好数据备份和回滚方案。加密改造本质上是一次“数据写入链路变更”一旦上线后密钥配置错误最坏情况下可能所有新增数据都无法写入。提前准备一个开关配置里支持queryWithCipherColumn和明文列同时保留这样随时可以回退成明文模式把影响降到最低。第三如果你的项目里还没有 ShardingJDBC仅仅为了做字段加密而引入它要慎重评估。ShardingJDBC 的全套 SQL 解析和改写机制确实会带来一定的复杂度和性能损耗。如果只是单一数据库、单表单库业务也不复杂直接在应用层做一个轻量级 AOP 切面做字段加密或者使用专门的字段加密组件可能更简单可控。ShardingJDBC 的优势在于“加密分库分表读写分离”一体化如果你的系统未来有分片需求那提前引入用它做加密就是顺理成章的事。我在实际项目中见过几个团队因为赶合规进度直接把 ShardingJDBC 引入到一个单体小系统里结果 SQL 解析报错、事务边界调整、数据源配置冲突接连出现最后反而拖慢了上线进度。选型永远是“够用就好”不要为了技术而技术。数据加密这件事做起来不难难的是一直不出错。ShardingJDBC 把加解密从业务代码中解耦出来让团队能用统一、可控的方式推进敏感字段保护这本身就比“人肉加密”强得多。如果你正在做安全改造建议先拿一张测试表跑通整个链路再把方案逐步铺开。踩过几次坑之后你会发现它比想象中更值得用。