fastjson 1.2 版本之前的bug, 反序列化时自动排序,导致签名不过

大家好,我是烤鸭:
今天分享一个问题,使用fastjson 导致签名不过。

1.  问题复现:


fastjson 1.2.4
获取返回值:

{"data":[{"id":"120190422110857284042111114","bankAccountType":"DEBIT","isMainCard":"0","bindId":"120190422110857284042111114","bankCardPhone":"15010311111","realName":null,"partBankAccountNo":"1199","bindChannel":"11111_PROTOCOL","masterAccountNo":null,"branchBankName":"建设银行","bankCode":"CCB"}],"sign":"nIBnR5YpoiYg4CwYIs5l1K2ne30X7A55YtjJH9teHYcZkRcPcI/ECg/TzKiMCFtKssOk1F8hXjC3Of2NhgYcduZAw4VjBb4HMuz9qRgSlZht0CXsMh8RVyysbJcKe8zhd4mansWzlxnIK2Kh3YnKf9Hzd/cHlcczr6UnDeifbZk="}

获取返回值中的数组:
 

[{"bankCode":"CCB","bankAccountType":"DEBIT","branchBankName":"建设银行","partBankAccountNo":"1199","id":"120190422110857284042111114","bindChannel":"11111_PROTOCOL","isMainCard":"0","bindId":"120190422110857284042111114","bankCardPhone":"15010311111"}]

fastjson 1.1.32
获取返回值:

{"data":[{"id":"120190422110857284042111114","bankAccountType":"DEBIT","isMainCard":"0","bindId":"120190422110857284042111114","bankCardPhone":"15010311111","realName":null,"partBankAccountNo":"1199","bindChannel":"11111_PROTOCOL","masterAccountNo":null,"branchBankName":"建设银行","bankCode":"CCB"}],"sign":"nIBnR5YpoiYg4CwYIs5l1K2ne30X7A55YtjJH9teHYcZkRcPcI/ECg/TzKiMCFtKssOk1F8hXjC3Of2NhgYcduZAw4VjBb4HMuz9qRgSlZht0CXsMh8RVyysbJcKe8zhd4mansWzlxnIK2Kh3YnKf9Hzd/cHlcczr6UnDeifbZk="}

获取返回值中的数组:

[{"bankAccountType":"DEBIT","bankCardPhone":"15010311111","bankCode":"CCB","bindChannel":"11111_PROTOCOL","bindId":"120190422110857284042111114","branchBankName":"建设银行","id":"120190422110857284042111114","isMainCard":"0","partBankAccountNo":"1199"}]

由于引入第三方的jar包,在返回值验签的时候报错。第三方jar中使用的fastjson 版本是 1.1.32,而我们项目中使用的是 1.2.4。
可以看到取出数组数据之后的不同点。一个是按字母升序排列的,一个是按自然排序(放入的顺序)。真的是坑....

2.  源码分析:

fastjson 1.2.4 

com.alibaba.fastjson.serializer.ListSerializer
代码更简洁了,去掉了对array的排序功能

public final void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features)
                                                                                                       throws IOException {

        boolean writeClassName = serializer.isEnabled(SerializerFeature.WriteClassName);

        SerializeWriter out = serializer.getWriter();

        Type elementType = null;
        if (writeClassName) {
            if (fieldType instanceof ParameterizedType) {
                ParameterizedType param = (ParameterizedType) fieldType;
                elementType = param.getActualTypeArguments()[0];
            }
        }

        if (object == null) {
            if (out.isEnabled(SerializerFeature.WriteNullListAsEmpty)) {
                out.write("[]");
            } else {
                out.writeNull();
            }
            return;
        }

        List<?> list = (List<?>) object;

        if (list.size() == 0) {
            out.append("[]");
            return;
        }

        SerialContext context = serializer.getContext();
        serializer.setContext(context, object, fieldName, 0);

        ObjectSerializer itemSerializer = null;
        try {
            if (out.isEnabled(SerializerFeature.PrettyFormat)) {
                out.append('[');
                serializer.incrementIndent();

                int i = 0;
                for (Object item : list) {
                    if (i != 0) {
                        out.append(',');
                    }

                    serializer.println();
                    if (item != null) {
                        if (serializer.containsReference(item)) {
                            serializer.writeReference(item);
                        } else {
                            itemSerializer = serializer.getObjectWriter(item.getClass());
                            SerialContext itemContext = new SerialContext(context, object, fieldName, 0, 0);
                            serializer.setContext(itemContext);
                            itemSerializer.write(serializer, item, i, elementType, 0);
                        }
                    } else {
                        serializer.getWriter().writeNull();
                    }
                    i++;
                }

                serializer.decrementIdent();
                serializer.println();
                out.append(']');
                return;
            }

            out.append('[');
            int i = 0;
            for (Object item : list) {
                if (i != 0) {
                    out.append(',');
                }
                
                if (item == null) {
                    out.append("null");
                } else {
                    Class<?> clazz = item.getClass();

                    if (clazz == Integer.class) {
                        out.writeInt(((Integer) item).intValue());
                    } else if (clazz == Long.class) {
                        long val = ((Long) item).longValue();
                        if (writeClassName) {
                            out.writeLongAndChar(val, 'L');
                        } else {
                            out.writeLong(val);
                        }
                    } else {
                        SerialContext itemContext = new SerialContext(context, object, fieldName, 0, 0);
                        serializer.setContext(itemContext);

                        if (serializer.containsReference(item)) {
                            serializer.writeReference(item);
                        } else {
                            itemSerializer = serializer.getObjectWriter(item.getClass());
                            itemSerializer.write(serializer, item, i, elementType, 0);
                        }
                    }
                }
                i++;
            }
            out.append(']');
        } finally {
            serializer.setContext(context);
        }
    }

fastjson 1.1.32
StringDeserializer

public static <T> T deserialze(DefaultJSONParser parser) {
    final JSONLexer lexer = parser.getLexer();
    if (lexer.token() == JSONToken.LITERAL_STRING) {
        String val = lexer.stringVal();
        lexer.nextToken(JSONToken.COMMA);
        return (T) val;
    }
    
    if (lexer.token() == JSONToken.LITERAL_INT) {
        String val = lexer.numberString();
        lexer.nextToken(JSONToken.COMMA);
        return (T) val;
    }

    Object value = parser.parse();

    if (value == null) {
        return null;
    }

    return (T) value.toString();
}

 public Object parse(Object fieldName) {
    final JSONLexer lexer = getLexer();
    switch (lexer.token()) {
        case SET:
            lexer.nextToken();
            HashSet<Object> set = new HashSet<Object>();
            parseArray(set, fieldName);
            return set;
        case TREE_SET:
            lexer.nextToken();
            TreeSet<Object> treeSet = new TreeSet<Object>();
            parseArray(treeSet, fieldName);
            return treeSet;
        case LBRACKET:
            JSONArray array = new JSONArray();
            parseArray(array, fieldName);
            return array;
        case LBRACE:
            JSONObject object = new JSONObject();
            return parseObject(object, fieldName);
        case LITERAL_INT:
            Number intValue = lexer.integerValue();
            lexer.nextToken();
            return intValue;
        case LITERAL_FLOAT:
            Object value = lexer.decimalValue(isEnabled(Feature.UseBigDecimal));
            lexer.nextToken();
            return value;
        case LITERAL_STRING:
            String stringLiteral = lexer.stringVal();
            lexer.nextToken(JSONToken.COMMA);

            if (lexer.isEnabled(Feature.AllowISO8601DateFormat)) {
                JSONScanner iso8601Lexer = new JSONScanner(stringLiteral);
                try {
                    if (iso8601Lexer.scanISO8601DateIfMatch()) {
                        return iso8601Lexer.getCalendar().getTime();
                    }
                } finally {
                    iso8601Lexer.close();
                }
            }

            return stringLiteral;
        case NULL:
            lexer.nextToken();
            return null;
        case TRUE:
            lexer.nextToken();
            return Boolean.TRUE;
        case FALSE:
            lexer.nextToken();
            return Boolean.FALSE;
        case NEW:
            lexer.nextToken(JSONToken.IDENTIFIER);

            if (lexer.token() != JSONToken.IDENTIFIER) {
                throw new JSONException("syntax error");
            }
            lexer.nextToken(JSONToken.LPAREN);

            accept(JSONToken.LPAREN);
            long time = ((Number) lexer.integerValue()).longValue();
            accept(JSONToken.LITERAL_INT);

            accept(JSONToken.RPAREN);

            return new Date(time);
        case EOF:
            if (lexer.isBlankInput()) {
                return null;
            }
            throw new JSONException("unterminated json string, pos " + lexer.getBufferPosition());
        case ERROR:
        default:
            throw new JSONException("syntax error, pos " + lexer.getBufferPosition());
    }
}

重点在于:

case LBRACKET:
            JSONArray array = new JSONArray();
            parseArray(array, fieldName);
            return array;

DefaultJSONParser:

public final void parseArray(final Collection array, Object fieldName) {
    final JSONLexer lexer = getLexer();

    if (lexer.token() == JSONToken.SET || lexer.token() == JSONToken.TREE_SET) {
        lexer.nextToken();
    }

    if (lexer.token() != JSONToken.LBRACKET) {
        throw new JSONException("syntax error, expect [, actual " + JSONToken.name(lexer.token()) + ", pos "
                                + lexer.pos());
    }

    lexer.nextToken(JSONToken.LITERAL_STRING);

    ParseContext context = this.getContext();
    this.setContext(array, fieldName);
    try {
        for (int i = 0;; ++i) {
            if (isEnabled(Feature.AllowArbitraryCommas)) {
                while (lexer.token() == JSONToken.COMMA) {
                    lexer.nextToken();
                    continue;
                }
            }

            Object value;
            switch (lexer.token()) {
                case LITERAL_INT:
                    value = lexer.integerValue();
                    lexer.nextToken(JSONToken.COMMA);
                    break;
                case LITERAL_FLOAT:
                    if (lexer.isEnabled(Feature.UseBigDecimal)) {
                        value = lexer.decimalValue(true);
                    } else {
                        value = lexer.decimalValue(false);
                    }
                    lexer.nextToken(JSONToken.COMMA);
                    break;
                case LITERAL_STRING:
                    String stringLiteral = lexer.stringVal();
                    lexer.nextToken(JSONToken.COMMA);

                    if (lexer.isEnabled(Feature.AllowISO8601DateFormat)) {
                        JSONScanner iso8601Lexer = new JSONScanner(stringLiteral);
                        if (iso8601Lexer.scanISO8601DateIfMatch()) {
                            value = iso8601Lexer.getCalendar().getTime();
                        } else {
                            value = stringLiteral;
                        }
                        iso8601Lexer.close();
                    } else {
                        value = stringLiteral;
                    }

                    break;
                case TRUE:
                    value = Boolean.TRUE;
                    lexer.nextToken(JSONToken.COMMA);
                    break;
                case FALSE:
                    value = Boolean.FALSE;
                    lexer.nextToken(JSONToken.COMMA);
                    break;
                case LBRACE:
                    JSONObject object = new JSONObject();
                    value = parseObject(object, i);
                    break;
                case LBRACKET:
                    Collection items = new JSONArray();
                    parseArray(items, i);
                    value = items;
                    break;
                case NULL:
                    value = null;
                    lexer.nextToken(JSONToken.LITERAL_STRING);
                    break;
                case RBRACKET:
                    lexer.nextToken(JSONToken.COMMA);
                    return;
                default:
                    value = parse();
                    break;
            }

            array.add(value);
            checkListResolve(array);

            if (lexer.token() == JSONToken.COMMA) {
                lexer.nextToken(JSONToken.LITERAL_STRING);
                continue;
            }
        }
    } finally {
        this.setContext(context);
    }
}

com.alibaba.fastjson.serializer.SerializeWriter

writeStringWithDoubleQuote()   重排序

 System.arraycopy(buf, lastSpecialIndex + 1, buf, lastSpecialIndex + 2, end - lastSpecialIndex - 1);
            buf[lastSpecialIndex] = '\\';
            buf[++lastSpecialIndex] = replaceChars[(int) lastSpecial];

小结:


由于 fastjson 版本 1.1.32 在反序列化的时候,自动排序了。而 1.2.4 以后的版本没有自动排序,如果加解密的json 版本不一样,就会导致加解密出错。
去阿里的仓库找fastjson,发现已经没有 1.2.4之前的版本了,说明之前的版本确实有问题。

发布了115 篇原创文章 · 获赞 58 · 访问量 23万+

猜你喜欢

转载自blog.csdn.net/Angry_Mills/article/details/97651075