Spring Boot Implementation of Sensitive Word and Special Character Filtering

  • 2021-10-13 07:23:55
  • OfStack

Background:

The technology adopts Spring Boot, the request method is mainly POST, and the annotation used more frequently is @ RequestBody

Deliver it to the tester for testing, and the tester searches for various special characters in the fuzzy search module, so that both sensitive words and special characters will be put into storage.

For developers with feelings like me, it is intolerable.

Come up and do it! Mainly adopted


@ControllerAdvice(basePackages = "com.my") 

To process the data submitted by users.

The following is the sample code, which does not affect the functional implementation of the author's statement:


/**
 * @author Ryan
 * @date 2019/4/25 18:41
 */
@ControllerAdvice(basePackages = "com.ytkj")
public class EscapeSensitiveWordFilter implements RequestBodyAdvice {
    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        return inputMessage;
    }
    @Override
    public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        if(o != null){
            SensitiveWordUtils.apply(o);
        }
        return o;
    }
    @Override
    public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return o;
    }
}

Since we mainly deal with the submitted data, the main entrance is SensitiveWordUtils. apply (o); The "Object" parameter here is actually the entity with @ RequestBody typed in our Controller method parameter. We can directly here, using 1 means to deal with it.

The means here can only use reflection (if the reader has any good scheme can tell me).

1. String replacement;

2. Custom throw runtime exceptions;

Another advantage of doing this is that sensitive words can be managed here.

If you use replaceAll, it will be more difficult to manage the system 1.

Finally, the author puts his reflection below for reference only, and writes a "test" as the mark to replace the entry in the sensitive word replacement part.

Welcome bosses from all walks of life to straighten up!


import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
 * @author Ryan
 * @date 2019/4/26 12:40
 */
public class SensitiveWordUtils {
    /**
     * @param result
     * @return
     */
    public static Object apply(Object result) {
        if (result == null) {
            return null;
        }
        objectParse(result);
        return result;
    }
    /**
     * @param obj
     */
    public static void objectParse(Object obj) {
        List<Field> allField = findAllField(obj);
        for (Field field : allField) {
            field.setAccessible(true);
            Class<?> typeClazz = field.getType();
            matchFieldType(obj, field, typeClazz);
        }
    }
    public static List<Field> findAllField(Object object){
        List<Field> result = new ArrayList<>();
        Class<?> clazz = object.getClass();
        while (true) {
            clazz = clazz.getSuperclass();
            if (clazz == Object.class) {
                break;
            }
            Field[] declaredFields = clazz.getDeclaredFields();
            result.addAll(Arrays.asList(declaredFields));
        }
        return result;
    }
    /**
     * @param obj
     * @param field
     * @param clazz
     */
    public static <T> void matchFieldType(Object obj, Field field, T clazz) {
        try {
            T param = (T) field.get(obj);
            if(param == null){
                return;
            }
            if (clazz == List.class) {
                List p = (List)param;
                for (Object o : p) {
                    objectParse(o);
                }
            } else if (clazz == String.class) {
                setValue(obj, field, " Test ");
            } else if (clazz == Map.class) {
                Map map = (Map)param;
                for (Object o : map.keySet()) {
                    objectParse(o);
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    /**
     *
     * @param object
     * @param field
     * @param param
     * @throws IllegalAccessException
     */
    public static void setValue(Object object, Field field, Object param) throws IllegalAccessException {
        if(!field.isAccessible()){
            throw new IllegalAccessException("modify the field fail.");
        }
        field.set(object, param);
    }
}

Here SensitiveWordUtils still has a lot of optimization point, I am not here at present just look at the effect, write very rough, hope great god don't spray.

Readers realize 1 by themselves, and I say 1 optimization point:

1. Cache String of object. Field or methodName of class type; On the first load, cache it; Put it on ConcurrentHashMap < ObjectType, List < StringField > > , do you feel refreshed a lot;

2. Filter out Field of String type, and consider other types as appropriate;

3. Wait for the male and female servants to think again;

Spring Boot Series 1 Sensitive Word Filter demo

Handling before serialization of objects

For example, springframework Framework (responseBody) json Format:


org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyAdviceChain#beforeBodyWrite 

Conversion of object data in.


@ControllerAdvice
@Slf4j
public class ShanDongShengYuHandler implements ResponseBodyAdvice { 
    @Autowired
    private ObjectMapper objectMapper; 
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }
 
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        ResponseData d = new ResponseData();
        sensitiveHidden(body);
        d.setData(body);
        return d;
    } 
 
    /**
     *  It only supports sensitive word filtering of custom type data, considering recursive performance 
     */
    private void sensitiveHidden(Object body) {
        if(body==null || StringUtils.isBlank(body.getClass().getName()) || !body.getClass().getName().contains(" Shandong ")){
            return;
        }
        Field[] declaredFields = body.getClass().getDeclaredFields();
        for (Field declaredField : declaredFields) {
            SensitiveWorldHidden annotation = declaredField.getAnnotation(SensitiveWorldHidden.class);
            log.warn(" "Annotation Type" {}",annotation);
            try {
                declaredField.setAccessible(true);
                Object o = declaredField.get(body);
                if(annotation != null) {
                    String content = objectMapper.writeValueAsString(o);
                    content = content.replace(" Garbage ", "**");
                    Object replaced = objectMapper.readValue(content, o.getClass());
                    declaredField.set(body, replaced);
                }else {
                    sensitiveHidden(o);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Related articles: