Spring - 若依框架操作日志入参字段过滤

需求:

一个接口入参字段有三个,其中一个字段传入的是base64码值,因为若依框架的操作日志设置了仅保存前2000个字符。导致入参转JsonObject对象时,排序在base64码字段后的字段,在操作日志入参列中丢失。

代码示例
@Data
public class customerParam {

    private Integer number;
    
    private String shortStr;
    
    // base64码值字段
    private String base64Str;
}
/**
  * 获取请求的参数,放到log中
  * 
  * @param operLog 操作日志
  * @throws Exception 异常
  */
private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception {
    String requestMethod = operLog.getRequestMethod();
    if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
    {
        String params = argsArrayToString(joinPoint.getArgs());
        // 仅保存参数文本的前2000个字符,base64值远超2000
        operLog.setOperParam(StringUtils.substring(params, 0, 2000));
    } else {
        Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
        operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
     }
}

起初以为跟传入字段顺序有关,经过调试后发现。在joinPoint.getArgs()时,字段在对象中还是按字段顺序排序的。在经过argsArrayToString方法后,才变为非字段顺序排序。

	/**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray)
    {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0)
        {
            for (Object o : paramsArray)
            {
                if (StringUtils.isNotNull(o) && !isFilterObject(o))
                {
                    try
                    {
                    	// 经过该行代码,字段顺序错乱,既不符合字母排序也不符合字段排序。
                        Object jsonObj = JSON.toJSON(o);
                        
                        params += jsonObj.toString() + " ";
                    }
                    catch (Exception e)
                    {
                    }
                }
            }
        }
        return params.trim();
    }

Object o中的字段也依然是按字段顺序排序的,在JSON.toJSON(o)执行后,对象字段改变了。

package com.alibaba.fastjson;
public abstract class JSON implements JSONStreamAware, JSONAware {

	/**
     * This method serializes the specified object into its equivalent representation as a tree of
     * {@link JSONObject}s. 
     *
     */
    public static Object toJSON(Object javaObject) {
        return toJSON(javaObject, SerializeConfig.globalInstance);
    }

	    @SuppressWarnings("unchecked")
    public static Object toJSON(Object javaObject, SerializeConfig config) {
        if (javaObject == null) {
            return null;
        }

        if (javaObject instanceof JSON) {
            return javaObject;
        }

        if (javaObject instanceof Map) {
            Map<Object, Object> map = (Map<Object, Object>) javaObject;

            int size = map.size();

            Map innerMap;
            if (map instanceof LinkedHashMap) {
                innerMap = new LinkedHashMap(size);
            } else if (map instanceof TreeMap) {
                innerMap = new TreeMap();
            } else {
                innerMap = new HashMap(size);
            }

            JSONObject json = new JSONObject(innerMap);

            for (Map.Entry<Object, Object> entry : map.entrySet()) {
                Object key = entry.getKey();
                String jsonKey = TypeUtils.castToString(key);
                Object jsonValue = toJSON(entry.getValue(), config);
                json.put(jsonKey, jsonValue);
            }

            return json;
        }

        if (javaObject instanceof Collection) {
            Collection<Object> collection = (Collection<Object>) javaObject;

            JSONArray array = new JSONArray(collection.size());

            for (Object item : collection) {
                Object jsonValue = toJSON(item, config);
                array.add(jsonValue);
            }

            return array;
        }

        if (javaObject instanceof JSONSerializable) {
            String json = JSON.toJSONString(javaObject);
            return JSON.parse(json);
        }

        Class<?> clazz = javaObject.getClass();

        if (clazz.isEnum()) {
            return ((Enum<?>) javaObject).name();
        }

        if (clazz.isArray()) {
            int len = Array.getLength(javaObject);

            JSONArray array = new JSONArray(len);

            for (int i = 0; i < len; ++i) {
                Object item = Array.get(javaObject, i);
                Object jsonValue = toJSON(item);
                array.add(jsonValue);
            }

            return array;
        }

        if (ParserConfig.isPrimitive2(clazz)) {
            return javaObject;
        }

        ObjectSerializer serializer = config.getObjectWriter(clazz);
        if (serializer instanceof JavaBeanSerializer) {
            JavaBeanSerializer javaBeanSerializer = (JavaBeanSerializer) serializer;

            JSONType jsonType = javaBeanSerializer.getJSONType();
            boolean ordered = false;
            if (jsonType != null) {
                for (SerializerFeature serializerFeature : jsonType.serialzeFeatures()) {
                    if (serializerFeature == SerializerFeature.SortField
                            || serializerFeature == SerializerFeature.MapSortField) {
                        ordered = true;
                    }
                }
            }

			// 当ordered为true时,才会按字段顺序排序。
            JSONObject json = new JSONObject(ordered);
            try {
                Map<String, Object> values = javaBeanSerializer.getFieldValuesMap(javaObject);
                for (Map.Entry<String, Object> entry : values.entrySet()) {
                    json.put(entry.getKey(), toJSON(entry.getValue(), config));
                }
            } catch (Exception e) {
                throw new JSONException("toJSON error", e);
            }
            return json;
        }
        
        String text = JSON.toJSONString(javaObject, config);
        return JSON.parse(text);
    }
}

new JSONObject() 当传入的参数是false时,创建的是HashMap;当传入的是true时,创建的是LinkedHashMap。
(1)LinkedHashMap保存插入顺序。双向链表维持循序。
(2)HashMap则是无序的。

public JSONObject(boolean ordered){
        this(DEFAULT_INITIAL_CAPACITY, ordered);
}
    
public JSONObject(int initialCapacity, boolean ordered){
        if (ordered) {
            map = new LinkedHashMap<String, Object>(initialCapacity);
        } else {
            map = new HashMap<String, Object>(initialCapacity);
        }
    }
解决方法

使用fastJson的 @JSONField(serialize=false) 注解过滤字段

@Data
public class customerParam {

    private Integer number;
    
    private String shortStr;
    
    // base64码值字段
    @JSONField(serialize=false)
    private String base64Str;
}

在入参字段上添加注解,代表不序列化某些字段。
加上该参数后,Object jsonObj = JSON.toJSON(o)后,仅剩余所需两个字段,但仍是乱序。

思考

1.当参数满足什么条件时,ordered才会是true。
解答:当对象配置@JSONType(serialzeFeatures = SerializerFeature.SortField)或@JSONTypeserializerFeature == SerializerFeature.MapSortField)时

2.为什么使用fastJson排序注解时,JSON.toJSON(o)排序无效。
解答:当实体类配置了类型和排序时有效。@JSONType(serialzeFeatures = SerializerFeature.SortField, orders = {“number”, “shortStr”, “base64Str”})

参考链接

JSONobject排序问题 — 微梦 https://zhuanlan.zhihu.com/p/578409387

指定JSON.toJSONString中实体类属性的输出顺序 — 八十萬空軍縂教頭
https://blog.csdn.net/weixin_42798722/article/details/118228267

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值