前段时间项目组打算把公司的一个老项目当做现有系统的子模块,现有系统的技术框架主要是采用springcloud,用redis来做session共享。老项目的用户鉴权采用jwt,鉴权成功后,会把对象存到session里面,当时为了尽量少动老项目的代码,老项目单独维护自己的用户对象,其他模块的用户对象则由用户服务模块统一提供。当时改造完后,访问老模块时候,报了如下的错误:
{"timestamp":1548981972259,"status":500,"error":"Internal Server Error","exception":"org.springframework.data.redis.serializer.SerializationException","message":"Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is org.springframework.core.NestedIOException: Failed to deserialize object type; nested exception is java.lang.ClassNotFoundException: xxx.model.UserInfoDTO","path":"/xxx/xx"}
0
当时这个错误还不是从控制台或者日志查出来的,而是通过fiddler抓包抓出来的。从错误的信息异常来看,是因为类找不到而导致redis反序列对象失败。问题点找出来了,接下来就是要解决,当时解决的问题的方法,有如下几种
1、老项目的用户对象也统一由用户服务模块提供
这种方案是可以解决用户对象反序列化失败问题,因为所有服务模块的用户对象都是来自用户服务模块,但因为老项目又有其他实体对象也用session来存放,所以上面方案只能解决用户对象问题,没法解决其他实体对象问题
2、老项目的实体对象都转换成json,存放到session,要用对象的话,再把json转成对象
这种方法是可以很完美的解决对象反序列化问题,但因为老项目有太多的实体对象都存放在session中,而项目组的成员对老项目的业务不了解,很容易改漏掉
3、老项目不进行session共享,单独维护自己的session
项目组后面采用的是这种方案,理由是
鉴权其核心代码如下
@Component
public class JwtFilter extends ZuulFilter {
private Logger logger = LoggerFactory.getLogger(JwtFilter.class);
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
logger.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString());
String requestUri = request.getRequestURI();
if(requestUri.startsWith("/Oxford")){
return true;
}
return false;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
HttpServletResponse response = ctx.getResponse();
Cookie[] cookies = request.getCookies();
Cookie jwtTokenCokie = null;
if(cookies != null){
for(Cookie cookie : cookies){
if(cookie.getName().equalsIgnoreCase(JwtConstant.JWT_COOKIE_NAME)){
jwtTokenCokie = cookie;
break;
}
}
}
if(jwtTokenCokie != null && jwtTokenCokie.getValue() != null && jwtTokenCokie.getValue().startsWith(JwtConstant
.JWT_COOKIE_VALUE_PRE)){
HttpSession httpSession = request.getSession(false);
if(httpSession != null){
// 已经登录,则根据时间,适当延长
Claims claims = JwtTokenUtil.getClaimsWithoutCheckTime(jwtTokenCokie.getValue().substring(JwtConstant
.JWT_COOKIE_VALUE_PRE.length()));
if (claims.getExpiration().before(DateUtils.addMinutes(new Date(), 15))) {
claims.setExpiration(DateUtils.addMinutes(new Date(), 30));
jwtTokenCokie.setValue(JwtConstant.JWT_COOKIE_VALUE_PRE+JwtTokenUtil.generateToken(claims));
jwtTokenCokie.setPath("/Oxford");
jwtTokenCokie.setHttpOnly(true);
response.addCookie(jwtTokenCokie);
}
}else{
// 没有登录的话,要将token时间改成超时
String expirationToken = JwtTokenUtil.refreshTokenWithoutCheckTime(jwtTokenCokie.getValue().substring(JwtConstant
.JWT_COOKIE_VALUE_PRE.length()), DateUtils.addDays(new Date(),
-2));
jwtTokenCokie.setValue(JwtConstant.JWT_COOKIE_VALUE_PRE+expirationToken);
jwtTokenCokie.setPath("/Oxford");
response.addCookie(jwtTokenCokie);
}
}
return null;
}
}
cookie配置其核心代码如下
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer();
defaultCookieSerializer.setCookieName("oxfordSessionId");
defaultCookieSerializer.setCookiePath("/Oxford");
return defaultCookieSerializer;
}
注:cookiePath要和contextPath配置一致,domainName没配置,默认同域
1、用fiddler来抓包挺好用的
2、当问题的解决方案很多时,要优先考虑项目所处的现状,比如是否适合大改动,研发的技术储备能力是否足够等