前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >springcloud用redis做session共享出现类反序列化失败问题

springcloud用redis做session共享出现类反序列化失败问题

作者头像
lyb-geek
发布2019-03-07 10:29:43
4.9K0
发布2019-03-07 10:29:43
举报
文章被收录于专栏:Linyb极客之路Linyb极客之路

前言

前段时间项目组打算把公司的一个老项目当做现有系统的子模块,现有系统的技术框架主要是采用springcloud,用redis来做session共享。老项目的用户鉴权采用jwt,鉴权成功后,会把对象存到session里面,当时为了尽量少动老项目的代码,老项目单独维护自己的用户对象,其他模块的用户对象则由用户服务模块统一提供。当时改造完后,访问老模块时候,报了如下的错误:

代码语言:javascript
复制
{"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

项目组后面采用的是这种方案,理由是

  • 老项目是采用jwt来进行鉴权,其实是可以脱离session
  • 老项目项目配置自己的contextPath,而不是采用默认的contextpath /
  • 根据开闭原则,对扩展开放,对修改关闭,如果要改造,可以做如下两件事情
  1. 在网关鉴权方面,多加一层jwt鉴权过滤
  2. 配置老项目的自己cookie

鉴权其核心代码如下

代码语言:javascript
复制
@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配置其核心代码如下

代码语言:javascript
复制
@Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer();
        defaultCookieSerializer.setCookieName("oxfordSessionId");
        defaultCookieSerializer.setCookiePath("/Oxford");
        return defaultCookieSerializer;
    }

注:cookiePath要和contextPath配置一致,domainName没配置,默认同域

总结

1、用fiddler来抓包挺好用的

2、当问题的解决方案很多时,要优先考虑项目所处的现状,比如是否适合大改动,研发的技术储备能力是否足够等

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-02-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Linyb极客之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 总结
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档