1. Introduction

In daily development, personal information such as ID number, mobile phone number, card number and customer number need data desensitization. Otherwise, it is easy to cause personal privacy disclosure, customer information disclosure, and give criminals an opportunity. But data desensitization isn’t about hiding sensitive information, it’s about looking real when it can’t be. In my previous company, due to the lack of attention to desensitization, an employee exported the core customer information through the background export function and sold it to rival products when he resigned, which caused great losses to the company. Of course, there are reasons for data management, but desensitization is still a part that can not be ignored. Desensitization can ensure the compliance use of data to a certain extent. Here is a desensitization data:

Mybatis desensitization plug-in

Recently, I am studying Mybatis plug-in, so I consider whether I can do desensitization in ORM, so I tried it, here to share my ideas. Take this to also share Mybatis plug-in development ideas.

2.1 Mybatis plug-in interface

Mybatis used in plug-in, need to implement the interface org. Apache. Ibatis. Plugin. The Interceptor, as shown below:

public interface Interceptor {

  Object intercept(Invocation invocation) throws Throwable;

  default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }

  default void setProperties(Properties properties) {
    // NOP}}Copy the code

In fact, the core of this is the Object Intercept (Invocation) method, which we need to implement.

2.2 the Invocation object

So what is the concept of the Core Method Invocation?

public class Invocation {

  private final Object target;
  private final Method method;
  private final Object[] args;

  public Invocation(Object target, Method method, Object[] args) {
    this.target = target;
    this.method = method;
    this.args = args;
  }

  public Object getTarget(a) {
    return target;
  }

  public Method getMethod(a) {
    return method;
  }

  public Object[] getArgs() {
    return args;
  }

  public Object proceed(a) throws InvocationTargetException, IllegalAccessException {
    returnmethod.invoke(target, args); }}Copy the code

This thing contains four concepts:

  • Target Intercepts the object
  • Method intercepts specific methods in target, which means the Mybatis plugin’s granularity is precise to the method level.
  • Parameter intercepted by arGS.
  • Proceed executes the intercepted method, and you can do something before and after execution.

2.3 Signature Interception

Now that we know that the granularity of the Mybatis plug-in is down to the method level, how does the plug-in know its turn to work?

So Mybatis has designed a signature mechanism to solve this problem by using the @intercepts annotation on the plug-in interface.

@Intercepts(@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}))
Copy the code

Just like the above, this is in effect a Invocation configured.

2.4 Scope of plug-ins

Which objects can the Mybatis plugin intercept, or at what lifecycle stage does the plugin work? It can intercept the following four objects:

  • Executor is an SQL Executor that contains a coarse-grained process for assembling parameters, assembling result sets to return values, and executing SQL.
  • StatementHandler is used to handle the execution of SQL, and it is very common to override SQL here.
  • ParameterHandler is used to handle incoming SQL parameters. We can override the rules for handling parameters.
  • ResultSetHandler is used to process result sets, and we can rewrite the assembly rules for result sets.

All you need to do is specify which of the four processing phases your business needs to intercept processing.

2.5 MetaObject

Mybatis provides a utility class org. Apache. Ibatis. Reflection. The MetaObject. It uses reflection to read and modify properties of important objects. We can use it to handle some properties of four objects, which is a common utility class developed by the Mybatis plug-in.

  • Object getValue(String Name) Obtains the attribute value of an Object by name. OGNL expressions are supported.
  • Void setValue(String name, Object Value) Sets the value of an attribute.
  • Class
    getSetterType(String name) Gets the input type of the setter method.
  • Class
    getGetterType(String name) Gets the return value type of the getter method.

We usually use SystemMetaObject. ForObject (Object Object) to instantiate MetaObject Object. You’ll see me use it in the next practical DEMO.

3. Mybatis desensitization plug-in combat

Next I put the beginning of desensitization needs to achieve. The desensitization field needs to be marked first and the desensitization strategy used is determined.

Write desensitization function:

/** * function of a specific policy *@author felord.cn
 * @sinceAfter * * /
public interface Desensitizer  extends Function<String.String>  {}Copy the code

Write an enumeration of desensitization strategies:

/** * desensitization strategy. **@author felord.cn
 * @since 11 :25
 */
public enum SensitiveStrategy {
    /** * Username sensitive strategy. */
    USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)"."$1 * $2")),
    /** * Id card sensitive type. */
    ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\w{4})"."$1 * * * * $2")),
    /** * Phone sensitive type. */
    PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})"."$1 * * * * $2")),

    /** * Address sensitive type. */
    ADDRESS(s -> s.replaceAll("(\\S{8})\\S{4}(\\S*)\\S{4}"."$1 $2 * * * * * * * *"));


    private final Desensitizer desensitizer;

    SensitiveStrategy(Desensitizer desensitizer) {
        this.desensitizer = desensitizer;
    }

    /**
     * Gets desensitizer.
     *
     * @return the desensitizer
     */
    public Desensitizer getDesensitizer(a) {
        returndesensitizer; }}Copy the code

Write markup annotations for desensitization fields:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sensitive {
    SensitiveStrategy strategy(a);
}
Copy the code

If a field in our return object needs to be desensitized, we just need to tag it. For example:

@Data
public class UserInfo {

    private static final long serialVersionUID = -8938650956516110149L;
    private Long userId;
    @Sensitive(strategy = SensitiveStrategy.USERNAME)
    private String name;
    private Integer age;
}
Copy the code

Then it’s time to write the plug-in. What I can confirm is that the handleResultSets method of the ResultSetHandler object needs to be intercepted. We just need to implement the plug-in interface Interceptor and add the signature. The whole logic is as follows:

@Slf4j
@Intercepts(@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}))
public class SensitivePlugin implements Interceptor {
    @SuppressWarnings("unchecked")
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        List<Object> records = (List<Object>) invocation.proceed();
        // desensitize the result set
        records.forEach(this::sensitive);
        return records;
    }


    private void sensitive(Object source) {
        // Get the return value typeClass<? > sourceClass = source.getClass();// Initialize the MetaObject of the return value type
        MetaObject metaObject = SystemMetaObject.forObject(source);
        // Capture the tag annotation @sensitive on the property and desensitize it accordingly
        Stream.of(sourceClass.getDeclaredFields())
                .filter(field -> field.isAnnotationPresent(Sensitive.class))
                .forEach(field -> doSensitive(metaObject, field));
    }


    private void doSensitive(MetaObject metaObject, Field field) {
        // Get the attribute name
        String name = field.getName();
        // Get the attribute value
        Object value = metaObject.getValue(name);
        // Only string types can be desensitized and cannot be null
        if(String.class == metaObject.getGetterType(name) && value ! =null) {
            Sensitive annotation = field.getAnnotation(Sensitive.class);
            // Obtain the corresponding desensitization policy and desensitization
            SensitiveStrategy type = annotation.strategy();
            Object o = type.getDesensitizer().apply((String) value);
            // Insert the desensitized value backmetaObject.setValue(name, o); }}}Copy the code

Then configure the desensitization plug-in to take effect:

@Bean
public SensitivePlugin sensitivePlugin(a){
    return new SensitivePlugin();
}
Copy the code

The query result is UserInfo(userId=123123, name= Li * Long, age=28), and the specified field is desensitized successfully.

As a side note, desensitization can also take place during JSON serialization.

4. To summarize

Today on the preparation of Mybatis plug-in some key points were explained, at the same time according to the description of a desensitization plug-in. However, it is important to familiarize yourself with the life cycles of the four major objects, or writing your own plug-ins can lead to unexpected results. The plugin can be accessed by following the wechat official account: code nongxiao Pangge reply keyword sensitive. If you find it useful please mercilessly like, look again, retweet.

Follow our public id: Felordcn for more information

Personal blog: https://felord.cn