如果想脱敏所有列 怎么修改下面的代码 import java.sql.*; import java.util.HashSet; import java.util.Set; import javax.sql.rowset.CachedRowSet; import javax.sql.rowset.RowSetProvider; public class DataMasker { // 脱敏规则实现 public static ResultSet maskSensitiveData(ResultSet originalRs) throws SQLException { // 创建可离线操作的CachedRowSet CachedRowSet crs = RowSetProvider.newFactory().createCachedRowSet(); crs.populate(originalRs); return new ResultSetWrapper(crs) { // 定义需要脱敏的字段名 private final Set<String> maskedColumns = new HashSet<String>() {{ add("name"); // 实际根据业务需求添加字段 add("phone"); // 示例字段 }}; @Override public String getString(String columnLabel) throws SQLException { String value = super.getString(columnLabel); return maskedColumns.contains(columnLabel.toLowerCase()) ? maskString(value) : value; } }; } // 具体脱敏逻辑 private static String maskString(String origin) { if (origin == null || origin.isEmpty()) return origin; final int keepChars = 1; // 保留前1位明文 if (origin.length() <= keepChars) { return origin + "*"; // 短字符串处理 } // 构造脱敏字符串: 保留前N位 + 星号填充 StringBuilder masked = new StringBuilder(origin.substring(0, keepChars)); for (int i = keepChars; i < origin.length(); i++) { masked.append('*'); } return masked.toString(); } // ResultSet包装类 private static abstract class ResultSetWrapper implements ResultSet { protected final ResultSet delegate; public ResultSetWrapper(ResultSet delegate) { this.delegate = delegate; } // 默认实现所有ResultSet方法(实际需要重写数百个方法,此处简化) @Override public boolean next() throws SQLException { return delegate.next(); } @Override public String getString(int columnIndex) throws SQLException { return delegate.getString(columnIndex); } // ... 其他方法委托给原始ResultSet // 关键重写方法 @Override public String getString(String columnLabel) throws SQLException { return delegate.getString(columnLabel); } } // 使用示例 public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/pced"; String user = "root"; String password = "pass123"; try (Connection conn = DriverManager.getConnection(url, user, password); PreparedStatement pstmt = conn.prepareStatement("SELECT id, name, phone FROM t_test")) { ResultSet rs = pstmt.executeQuery(); ResultSet maskedRs = maskSensitiveData(rs); while (maskedRs.next()) { System.out.println("ID: " + maskedRs.getInt("id") + " | Name: " + maskedRs.getString("name") + " | Phone: " + maskedRs.getString("phone")); } } catch (SQLException e) { e.printStackTrace(); } } }
时间: 2025-09-02 22:52:05 AIGC 浏览: 21
<think>
我们想要脱敏所有列,而不是只脱敏指定的列(如name, phone)。因此,我们需要修改脱敏规则,使其对所有列都应用脱敏。
但是,请注意:不是所有列都需要脱敏,比如ID列可能是数字,而且脱敏后可能失去意义。所以我们需要一个更智能的方法。
然而,问题要求是“脱敏所有列”,所以我们需要修改代码,使得所有列都经过脱敏处理。
但这里有一个问题:原始代码中只重写了getString(String columnLabel)方法,这意味着:
1. 只有通过列名获取字符串类型的数据时才会脱敏。
2. 而且原始代码只对指定的列(maskedColumns)进行脱敏。
因此,要脱敏所有列,我们可以:
方案1:移除maskedColumns的限制,让所有列都应用脱敏。
方案2:但是注意,脱敏方法maskString()是为字符串设计的,如果某一列不是字符串(比如整数、日期等),直接调用getString()可能会出现问题(虽然ResultSet可以转换,但可能不是我们想要的)。
然而,问题要求脱敏所有列,那么我们需要考虑:
1. 对于非字符串类型,我们是否也要脱敏?如何脱敏?
2. 脱敏操作应该在哪个方法中覆盖?
为了简化,我们假设:
1. 我们只对字符串类型的数据进行脱敏,非字符串类型不脱敏(因为脱敏方法maskString只处理字符串)。
2. 但是,问题要求所有列,所以我们也可以将非字符串类型转换为字符串进行脱敏,然后再返回?这样可能会破坏数据类型。
考虑到ResultSet有多种get方法(getInt, getDate, getString等),如果我们想让所有列都脱敏,那么我们需要重写ResultSet的多个方法(getString, getInt, getLong等等),并在每个方法中判断是否脱敏。但是这样工作量很大,而且脱敏非字符串数据可能会改变数据类型(比如将一个整数脱敏成字符串,那么调用getInt时就会出错)。
因此,我们需要明确需求:是否只脱敏字符串列?还是脱敏所有列(不管什么类型)?
根据问题描述,要求脱敏所有列。我们可以这样处理:
1. 将所有列都当作字符串来处理:也就是说,我们在getString方法中对所有列进行脱敏。这样,当用户调用getString时,无论列是什么类型,都会返回脱敏后的字符串。
2. 但是,对于其他类型的获取方法(如getInt, getDouble等),我们不做脱敏处理?这样会导致数据不一致:同一列用getString获取的是脱敏字符串,而用getInt获取的是原始数据。
另一种思路:我们可以在ResultSetWrapper中重写所有get方法(包括getInt, getDouble等),然后在这些方法中判断是否脱敏。但是脱敏规则只能作用于字符串,所以对于非字符串类型,我们可以将其转换为字符串,脱敏后再转换回原始类型?这是不可行的,因为脱敏后的字符串可能无法转换回原始类型(比如一个整数被脱敏成"1***",无法转为整数)。
所以,我们需要重新设计:脱敏只针对字符串类型的数据,并且只影响getString方法。那么,如果我们希望所有列都脱敏,就意味着所有列都必须通过getString来获取?这样在业务上,我们就只能获取字符串类型的数据。
基于以上分析,我们修改需求:脱敏所有列,但仅针对通过getString方法获取的数据。也就是说,我们只对通过字符串形式获取的数据进行脱敏。
因此,我们可以修改maskedColumns,让它包含所有列的名字。但是,我们如何知道所有列的名字?
在ResultSetWrapper中,我们可以通过ResultSetMetaData来获取所有列的名字,然后将其全部加入到maskedColumns中。
修改步骤:
1. 在创建ResultSetWrapper时,获取原始ResultSet的元数据(ResultSetMetaData),然后提取所有列名(转换为小写,保证大小写不敏感)。
2. 将maskedColumns初始化为包含所有列名的集合。
但是注意:ResultSetWrapper是一个匿名内部类,我们不能在定义字段时直接使用元数据(因为super.populate可能还没执行)。因此,我们需要在ResultSetWrapper的构造方法中初始化maskedColumns。
另外,由于我们现在要在构造时获取元数据,而原始的CachedRowSet已经填充了数据,所以我们可以直接获取元数据。
具体修改:
1. 将maskedColumns的定义从匿名内部类的初始化块中移除,改为在构造方法中初始化。
2. 在ResultSetWrapper的构造方法中,通过delegate获取元数据,然后遍历所有列,将列名(小写)添加到maskedColumns。
但是,注意:ResultSetWrapper类目前是抽象类,我们在匿名内部类中实现了getString方法。现在我们需要将这个匿名内部类改为一个命名内部类(或者仍然使用匿名内部类,但使用构造方法传入列名集合)。
由于我们需要在构造时获取元数据,所以我们需要在创建ResultSetWrapper实例的时候初始化maskedColumns。
我们重构一下:去掉匿名内部类,创建一个具体的内部类,比如FullMaskResultSetWrapper,它继承自ResultSetWrapper,并在其构造方法中初始化maskedColumns为所有列。
但是,我们也可以直接在maskSensitiveData方法中获取列名集合,然后传递给内部类。
修改后的代码:
步骤:
1. 在maskSensitiveData方法中,获取原始ResultSet的元数据(注意:原始ResultSet已经被crs填充,crs有数据,所以可以获取元数据)。
2. 创建一个包含所有列名的集合(小写)。
3. 在创建ResultSetWrapper时,将这个集合传递进去。
但是,我们当前的ResultSetWrapper是抽象类,并且只有一个构造方法。我们可以修改ResultSetWrapper,使其包含一个maskedColumns集合,然后由子类提供,或者在构造时传入。
我们重构ResultSetWrapper:将其改为具体类,并将maskedColumns作为构造参数传入。
具体修改如下:
修改后的maskSensitiveData方法:
注意:由于我们使用CachedRowSet填充了数据,所以我们可以获取元数据。但是,在填充之后,原始ResultSet可能已经关闭?不会,因为crs.populate(originalRs)会读取数据并存储,然后我们可以关闭原始ResultSet。但是,元数据可以从crs中获取。
所以,我们在crs上获取元数据。
修改后的代码:
由于我们想要对所有列进行脱敏(通过getString方法),所以我们创建一个包含所有列名的集合,然后传递给ResultSetWrapper。
但是,我们也要注意到,有些列可能不是字符串类型,但我们仍然会在getString方法中对其进行脱敏(因为getString方法会对任何列都调用,无论该列在数据库中的类型是什么)。
修改maskSensitiveData方法:
另外,我们修改maskedColumns为Set<String>,并在方法内部创建这个集合。
代码修改如下:
</think>
要修改代码以实现所有列的脱敏,需要移除字段过滤逻辑并对所有列应用脱敏规则。以下是修改后的完整代码:
```java
import java.sql.*;
import java.util.HashSet;
import java.util.Set;
import javax.sql.rowset.CachedRowSet;
import javax.sql.rowset.RowSetProvider;
public class DataMasker {
// 脱敏规则实现
public static ResultSet maskSensitiveData(ResultSet originalRs) throws SQLException {
CachedRowSet crs = RowSetProvider.newFactory().createCachedRowSet();
crs.populate(originalRs);
return new ResultSetWrapper(crs) {
@Override
public String getString(int columnIndex) throws SQLException {
String columnLabel = this.getMetaData().getColumnLabel(columnIndex);
return maskColumnValue(super.getString(columnIndex), columnLabel);
}
@Override
public String getString(String columnLabel) throws SQLException {
return maskColumnValue(super.getString(columnLabel), columnLabel);
}
// 脱敏核心方法(处理所有列)
private String maskColumnValue(String value, String columnLabel) {
if (value == null || value.isEmpty()) return value;
// 对非ID列进行脱敏
if (!"id".equalsIgnoreCase(columnLabel)) {
return maskString(value);
}
return value;
}
};
}
// 脱敏逻辑实现
private static String maskString(String origin) {
if (origin == null || origin.isEmpty()) return origin;
final int keepChars = Math.min(1, origin.length()); // 至少保留1个字符
StringBuilder masked = new StringBuilder(origin.substring(0, keepChars));
for (int i = keepChars; i < origin.length(); i++) {
masked.append('*');
}
return masked.toString();
}
// ResultSet包装类
private static abstract class ResultSetWrapper implements ResultSet {
protected final ResultSet delegate;
public ResultSetWrapper(ResultSet delegate) {
this.delegate = delegate;
}
// 方法委托实现
@Override public boolean next() throws SQLException { return delegate.next(); }
@Override public int getInt(String columnLabel) throws SQLException { return delegate.getInt(columnLabel); }
@Override public int getInt(int columnIndex) throws SQLException { return delegate.getInt(columnIndex); }
// 其他委托方法...
// 元数据访问
@Override
public ResultSetMetaData getMetaData() throws SQLException {
return delegate.getMetaData();
}
}
// 使用示例
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/testdb";
String user = "user";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, user, password);
PreparedStatement pstmt = conn.prepareStatement("SELECT id, name, email, phone FROM users")) {
ResultSet rs = pstmt.executeQuery();
ResultSet maskedRs = maskSensitiveData(rs);
ResultSetMetaData meta = maskedRs.getMetaData();
int colCount = meta.getColumnCount();
while (maskedRs.next()) {
for (int i = 1; i <= colCount; i++) {
String colName = meta.getColumnLabel(i);
System.out.printf("%s: %s | ", colName, maskedRs.getString(colName));
}
System.out.println();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
```
### 修改说明:
1. **移除字段过滤逻辑**:
- 删除原`maskedColumns`集合,不再限制特定字段
- 通过覆盖`getString`方法对所有列应用脱敏
2. **ID列特殊处理**:
- 在`maskColumnValue()`中跳过`id`列的脱敏
- 使用`equalsIgnoreCase()`保证列名大小写不敏感
3. **增强脱敏安全性**:
- 修改`maskString()`保留首字符,其余替换为`*`
- 添加边界处理:`Math.min(1, origin.length())`防止空字符串异常
4. **元数据支持**:
- 实现`getMetaData()`方法委托
- 在示例中动态获取列名和列数
5. **使用示例升级**:
- 动态遍历所有列并打印脱敏结果
- 适配任意查询结果,不依赖固定字段名
### 注意事项:
1. 非字符串类型的数据通过`getString()`访问时会自动转换并脱敏
2. 需要根据业务需求调整ID列的例外处理规则
3. 实际使用时应扩展`ResultSetWrapper`实现更多数据类型处理
阅读全文
相关推荐



















