关于Caused by: com.alibaba.fastjson.JSONException: syntax error, expect }, actual ,
Fastjson升级问题
目录
问题背景
Fastjson版本由于存在安全漏洞,公司要求各个部门对所属应用中存在低于1.2.83版本的fastjson升级应用升级到1.2.83,即低版本fastjson升级到高版本。
问题现象
Caused by: com.alibaba.fastjson.JSONException: syntax error, expect }, actual , |
问题堆栈信息
Method: checkLogin, exception: com.alibaba.fastjson.JSONException: syntax error, expect }, actual ,, dubbo version: 2.4.7, current host: 172.21.21.38 |
问题解决之路(思考方向)
复现问题
- 第一反应是从redis获取到字符串不满足fastjson格式
但是如果不是json格式,那么早就应该报问题了。
然后加日志,输出从redis中的取出的字符串,发现字符串格式正常且符合json格式:
[{"userInfo":"","jun":"","jum":"","nickName":"xxxxxx","openUserFlag":"open","userName":"xxxx","userId":"111111","yzToken":"","expiredTime":"1661338531800","token":"xxxxxx","moniPharmacistId":"","result":0,"userLeverId":1,"loginTime":"1661338531800","gltoken":"","jud":"","member":null,"userIp":"222.191.226.242","customer":{"registerIP":"","companyName":"","channel":"","source":10,"type":0,"password":"xxxxxx","province":"0","id":"xxxxxxx","modifyDate":null,"nickName":"13270162613","telephone":"","version":"","userLevelStartTime":null,"isOldUser":0,"realPassword":"","district":"","name":"","partnerType":0,"userType":"","enterCount":0,"requestTag":"","cardNumber":"","isBindMobileToWexin":0,"loginMobile":"xxxxxx","status":1,"income":0,"ipaddress":"127.0.0.1","gender":1,"city":"","openId":null,"ecUId":0,"loginEmail":"","isDeleted":0,"cellphone":"","promoCode":"","popType":0,"email":"","userLevelEndTime":null,"userLevelId":1,"createDate":{"date":21,"hours":11,"seconds":17,"month":4,"timezoneOffset":-480,"year":120,"minutes":59,"time":1590033557000,"day":4},"birthDay":null,"address":"","salt":"L1TIwT","partnerName":"","userscore":0,"baoGangUser":false,"ecUserId":xxxxxx,"userId":"","moniPharmacistId":"","lastLoginTime":null,"partnerId":""}}] |
2 . 本地复现问题
本地使用junit来模拟“将redis 存入用户数据,然后调用出错的代码取数据”这个流程,结果正常,正常,正常!结果怎么会正常呢?
开始怀疑是两个字符串不一样,对比发现字符串中createDate不一样。
json字符串中异常的createDate,如下:
正常的createDate,如下:
3. 怀疑json包混合使用导致(createDate怎么会是这种格式?)
createDate在类中定义的是java.util.Date类型,json成字符串怎么会是这种格式呢?
什么操作会导致它变成这样了(它到底经历了什么)?
开始怀疑net.sf.json包和fastjson混用导致的,经过验证发现,果然这种格式正是net.sf.json包中的处理方式(心里一万头羊驼奔驰)。
-
问题原因
net.sf.json包中的JSONArray和JSONObject会将java.util.Date类型的createDate格式化为json字符串,即
"createDate":{"date":31,"hours":18,"seconds":14,"month":4,"timezoneOffset":-480,"year":121,"minutes":53,"time":1622458394000,"day":1},
而我们实际需要的时long类型的值,或者指定日期格式的字符串。
总的来说:
项目中net.sf.json包和alibaba 混合使用,使用net.sf.json包中的类将对象格式化后存入到Redis,解析redis中的字符串却使用的fastjson。
那么怎么混用的呢?神奇的操作(听我说,谢谢你!!!)。
在一段业务中,存入redis中的用户信息使用net.sf.json包格式化为字符串的,如下:
public void setUserInfoToRedis(UserInfo userInfo, EcuserCustomer customer, int type) {
//此处的JSONArray是net.sf.json.JSONArray而不是fastjson中的
String value = String.valueOf(JSONArray.fromObject(userInfo));
if (type == 1) {
String key = customer.getId() + USERINFO;
int ttl = PassportApolloConfigUtil.getConfigIntVal("userinfoweek_expire");
RedisUtils.set(key, value, Long.valueOf(ttl));
} else {
set(customer.getId(), USERINFO, value);
}
}
而从redis中取出字符串后,使用alibaba的fastjson解析的,如下:
public static UserInfo toUserInfo(String value){
return com.alibaba.fastjson.JSONArray.parseArray(value).getObject(0,UserInfo.class);
}
目前看低版本支持net.sf.json和alibaba混用,但是高版本fastjson进行了优化,不再兼容,所以升级导致报错。
问题解决
通过兜底逻辑兼容解析json字符串。
(1)针对解析报错的字符串,我们就认为是net.sf.JSON包处理的,所以我们使用net.sf包下的解析。
(2)针对可以正常解析的的json字符串,我们还是使用alibaba fastjson来解析。
处理代码如下:
public static UserInfo toUserInfo(String value) {
log.info("从redis中获取的用户信息字符串userInfoStr={}", value);
UserInfo userInfo = null;
try {
userInfo = com.alibaba.fastjson.JSONArray.parseArray(value).getObject(0, UserInfo.class);
} catch (Exception e) {
log.error("使用fast json解析字符串异常,异常信息error={},尝试使用net.sf.json解析字符串。", e.getMessage());
JSONArray jsonArray = JSONArray.fromObject(value);
JSONObject jsonObject = (JSONObject) jsonArray.get(0);
userInfo = (UserInfo) JSONObject.toBean(jsonObject, UserInfo.class);
}
return userInfo;
}
参考博客
更多推荐
所有评论(0)