Skip to content
Menu
Portfolio
  • Artificial Intelligence
  • Personal Projects
  • Assignments
  • Algorithms
  • Notes
  • Home
Portfolio

TechPi Forum

Posted on July 12, 2025July 29, 2025

Sensitive word filtering system

Define a Custom Configuration Class:

@Data
@Component
@ConfigurationProperties(prefix = "picoding.sensitive")
public class SensitiveProperty {
    private Boolean enable;       // Enable or disable filtering
    private List<String> deny;    // Custom sensitive words
    private List<String> allow;   // Custom whitelisted words
}

Service with Dynamic Refresh Support:

@Service
public class SensitiveService {
    private SensitiveProperty sensitiveConfig;
    private volatile SensitiveWordBs sensitiveWordBs;

    public SensitiveService(DynamicConfigContainer dynamicConfigContainer, SensitiveProperty sensitiveConfig) {
        this.sensitiveConfig = sensitiveConfig;
        dynamicConfigContainer.registerRefreshCallback(sensitiveConfig, this::refresh);
    }

    @PostConstruct
    public void refresh() {
        IWordDeny deny = () -> {
            List<String> sub = WordDenySystem.getInstance().deny();
            sub.addAll(sensitiveConfig.getDeny());
            return sub;
        };

        IWordAllow allow = () -> {
            List<String> sub = WordAllowSystem.getInstance().allow();
            sub.addAll(sensitiveConfig.getAllow());
            return sub;
        };

        sensitiveWordBs = SensitiveWordBs.newInstance()
                .wordDeny(deny)
                .wordAllow(allow)
                .init();
        log.info("Sensitive word filter initialized!");
    }

    public boolean contains(String txt) {
        return BooleanUtils.isTrue(sensitiveConfig.getEnable()) && sensitiveWordBs.contains(txt);
    }

    public String replace(String txt) {
        return BooleanUtils.isTrue(sensitiveConfig.getEnable()) ? sensitiveWordBs.replace(txt) : txt;
    }

    public List<String> findAll(String txt) {
        return sensitiveWordBs.findAll(txt);
    }
}

Custom Database-Level Desensitization

In real-world production systems, certain sensitive fields like ID numbers, bank card numbers, etc., must not be stored in plaintext. These need to be encrypted before saving and decrypted when retrieved.

MyBatis Interceptor for Sensitive Field Replacement

Overall Idea:

  1. Add a custom annotation to fields in the DB entity class that require filtering.
  2. Create a query interceptor. When MyBatis returns results, check for fields with the annotation and replace them accordingly.

To optimize, metadata is cached to avoid repeated reflection checks.

Interceptor Implementation:

Two key steps:

  1. Parse entity class and detect annotated fields.
  2. Replace the sensitive content.
public Object intercept(Invocation invocation) throws Throwable {
        final List<Object> results = (List<Object>) invocation.proceed();

        if (results.isEmpty()) {
            return results;
        }

        final ResultSetHandler statementHandler = realTarget(invocation.getTarget());
        final MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        final MappedStatement mappedStatement = (MappedStatement) metaObject.getValue(MAPPED_STATEMENT);

        Optional firstOpt = results.stream().filter(Objects::nonNull).findFirst();
        if (!firstOpt.isPresent()) {
            return results;
        }
        Object firstObject = firstOpt.get();

        SensitiveObjectMeta sensitiveObjectMeta = findSensitiveObjectMeta(firstObject);

        replaceSensitiveResults(results, mappedStatement, sensitiveObjectMeta);
        return results;
    }

Metadata Construction:

Uses Java reflection to scan for @SensitiveField annotations on entity fields. These are stored for later processing.

public static Optional<SensitiveObjectMeta> buildSensitiveObjectMeta(Object param) {
        if (isNull(param)) {
            return Optional.empty();
        }

        Class<?> clazz = param.getClass();
        SensitiveObjectMeta sensitiveObjectMeta = new SensitiveObjectMeta();
        sensitiveObjectMeta.setClassName(clazz.getName());

        List<SensitiveFieldMeta> sensitiveFieldMetaList = newArrayList();
        sensitiveObjectMeta.setSensitiveFieldMetaList(sensitiveFieldMetaList);
        boolean sensitiveField = parseAllSensitiveFields(clazz, sensitiveFieldMetaList);
        sensitiveObjectMeta.setEnabledSensitiveReplace(sensitiveField);
        return Optional.of(sensitiveObjectMeta);
    }

Replace Sensitive Results:

private void replaceSensitiveResults(Collection<Object> results, MappedStatement mappedStatement, SensitiveObjectMeta sensitiveObjectMeta) {
        for (Object obj : results) {
            if (sensitiveObjectMeta.getSensitiveFieldMetaList() == null) {
                continue;
            }

            final MetaObject objMetaObject = mappedStatement.getConfiguration().newMetaObject(obj);
            sensitiveObjectMeta.getSensitiveFieldMetaList().forEach(i -> {
                Object value = objMetaObject.getValue(StringUtils.isBlank(i.getBindField()) ? i.getName() : i.getBindField());
                if (value == null) {
                    return;
                } else if (value instanceof String) {
                    String strValue = (String) value;
                    String processVal = sensitiveService.replace(strValue);
                    objMetaObject.setValue(i.getName(), processVal);
                } else if (value instanceof Collection) {
                    Collection listValue = (Collection) value;
                    if (CollectionUtils.isNotEmpty(listValue)) {
                        Optional firstValOpt = listValue.stream().filter(Objects::nonNull).findFirst();
                        if (firstValOpt.isPresent()) {
                            SensitiveObjectMeta valSensitiveObjectMeta = findSensitiveObjectMeta(firstValOpt.get());
                            if (Boolean.TRUE.equals(valSensitiveObjectMeta.getEnabledSensitiveReplace()) && CollectionUtils.isNotEmpty(valSensitiveObjectMeta.getSensitiveFieldMetaList())) {
                                replaceSensitiveResults(listValue, mappedStatement, valSensitiveObjectMeta);
                            }
                        }
                    }
                } else if (!ClassUtils.isPrimitiveOrWrapper(value.getClass())) {
                    SensitiveObjectMeta valSensitiveObjectMeta = findSensitiveObjectMeta(value);
                    if (Boolean.TRUE.equals(valSensitiveObjectMeta.getEnabledSensitiveReplace()) && CollectionUtils.isNotEmpty(valSensitiveObjectMeta.getSensitiveFieldMetaList())) {
                        replaceSensitiveResults(newArrayList(value), mappedStatement, valSensitiveObjectMeta);
                    }
                }
            });
        }
    }

Pages: 1 2 3 4 5

CATEGORIES

  • Artificial Intelligence
  • Personal Projects
  • Notes
  • Algorithms

  • University of Maryland
  • CMSC426 - Computer Vision
  • CMSC320 - Introduction to Data Science
  • CMSC330 - Organization of Programming Languages
  • CMSC216 - Introduction to Computer Systems
©2025 Portfolio | WordPress Theme by Superbthemes.com