起因
近期,我们的项目暴露出了一些问题,就像FastJSON反序列化漏洞,Tomcat拒绝服务漏洞,Shiro身份认证绕过漏洞,Apache Log4j拒绝服务漏洞,以及MySQL JDBC XXE漏洞等。当时我们以为,只需要更新一下版本,就能轻松解决问题。但是,万万没想到的是,这次的版本升级却引发了一个新的问题。
漏洞名称 | 漏洞描述 | 修复建议 |
---|---|---|
Oracle MySQL JDBC XXE漏洞(CVE-2021-2471) | Oracle MySQL 的 MySQL Connectors 产品中存在XML外部实体注入漏洞,该漏洞是由于MySQL JDBC 存在getSource() 方法未对传入的XML数据做校验,导致攻击者可以在XML数据中引入外部实体,造成XXE攻击。 | 将 mysql-connector-java 升级到 8.0.27 及以上版本,下载地址:https://mvnrepository.com/artifact/mysql/mysql-connector-java |
问题复现
表结构
版本
8.0.17
{
"code": "1",
"data": {
"create_time": 1641169164000
}
}
版本
8.0.33
{
"code": "1",
"data": {
"create_time": [2022, 1, 3, 8, 19, 24]
}
}
因为我在代码里加了个通过SimpleModule,在序列化转Json时,将所有的long变成string的处理,所以会报出下面的问题。
{
"data": {},
"msg": "Type definition error: [simple type, class java.time.LocalDateTime];
nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.LocalDateTime` not supported by default:
add Module \"com.fasterxml.jackson.datatype:jackson-datatype-jsr310\" to enable handling (through reference chain: Result[\"data\"]->java.util.HashMap[\"update_time\"])",
"code": -1
}
版本升级
当升级了MySQL Connector/J的版本后,从数据库读取的datetime数据类型发生了变化。原本是Timestamp类型的值,现在变成了LocalDateTime类型。
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
升级后
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
如果想使用更高的版本,请注意groupId,artifactId的更改。这里官网给出了解释。
MySQL 连接器/J 8.1 发行说明
MySQL 连接器/J 8.1.0 中的更改(2023-07-18,正式发布)
- Important Change: To comply with proper naming guidelines, the Maven groupId and artifactId for Connector/J have been changed to the following since release 8.0.31:
- groupId: com.mysql
- artifactId: mysql-connector-j
The old groupId and artifactId can no longer be used for linking the Connector/J library starting with this release. Please make sure you switch to the new coordinates. See Installing Connector/J Using Maven. (WL #15259)
解决方案
方法一:注解指定
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date createTime;
只适用于在实体上添加指定。
方法二:sql格式化
DATE_FORMAT(create_time, '%Y-%m-%d %H:%i:%s') AS create_time
方法三:Jackson重写信息转换器
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@EnableWebMvc
@Configuration
public class WebDataConvertConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
/**
* 序列换成json时,将所有的long变成string
* 因为js中得数字类型不能包含所有的java long值
*/
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(Long.class, ToStringSerializer.instance);
javaTimeModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
/* 格式化日期
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
objectMapper.setTimeZone(TimeZone.getTimeZone(ZoneId.of("Asia/Shanghai")));
*/
objectMapper.registerModule(javaTimeModule);
jackson2HttpMessageConverter.setObjectMapper(objectMapper);
converters.add(jackson2HttpMessageConverter);
}
}
在尝试将时间转换为JSON格式时,我遇到了一个令人困惑的问题。时间返回格式变成了时间戳格式或UTC时间格式,但我一直无法成功格式化它。网上搜索结果是因为spring.jackson.date-format 这个属性在spring boot 1.x中有用,但是当你项目升级到spring boot2.x 以后,时间格式化变无效了。尝试一下,没有解决,所以改用方法四,最终解决了这个问题。
{
"code": "1",
"data": {
"create_time": "2022-01-03T08:19:24"
}
}
方法四:FastJson重写信息转换器
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.ToStringSerializer;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@EnableWebMvc
@Configuration
public class WebDataConvertConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//创建fastJson消息转换器
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
//创建配置类
FastJsonConfig fastJsonConfig = new FastJsonConfig();
//修改配置返回内容的过滤
fastJsonConfig.setSerializerFeatures(SerializerFeature.BrowserCompatible,
SerializerFeature.WriteNullListAsEmpty,
SerializerFeature.PrettyFormat,
SerializerFeature.WriteDateUseDateFormat,
SerializerFeature.WriteNullStringAsEmpty,
SerializerFeature.WriteMapNullValue,
SerializerFeature.DisableCircularReferenceDetect
);
//解决Long转json精度丢失的问题
SerializeConfig serializeConfig = SerializeConfig.globalInstance;
serializeConfig.put(Long.class, ToStringSerializer.instance);
serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
fastJsonConfig.setSerializeConfig(serializeConfig);
// 将配置设置给转换器并添加到HttpMessageConverter转换器列表中
fastConverter.setFastJsonConfig(fastJsonConfig);
converters.add(fastConverter);
}
}
人因梦想而伟大,又因坚持梦想而成长!