ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

shiro SimpleSession的序列化

2021-11-22 20:02:01  阅读:156  来源: 互联网

标签:return SimpleSession transient private session new 序列化 shiro


不知道有没有人会问这个序列化它,能有什么用。

假如你和我一样从一个有历史背景的项目中 “脱颖而杀出一条血路” 的话,你可能会觉得这个有用【笑哭了的表情】

项目用的 Shiro,权限 + session 强绑定的技术背景,应用服务通过依赖 shiro sdk 而多点访问 redis 读取session 的业务现状(redisSessionDao 技术兄弟们对这个可能不陌生),多点访问 redis 意味着session 协议升级,与应用服务迭代的相互影响,以及可能会存在小机率的事务性问题(说到这终于想起来为什么经常有人反馈些,顽固的、莫名其妙的、用户状态问题),

直入技术要害:默认的 session 叫 SimpleSession ,它有几个属性是 transient 修饰的,贴出来方便交流:

private transient Serializable id;
private transient Date startTimestamp;
private transient Date stopTimestamp;
private transient Date lastAccessTime;
private transient long timeout;
private transient boolean expired;
private transient String host;
private transient Map<Object, Object> attributes;

受 transient 的影响 ,所以如果我们想把 SimpleSession 直接交给 feignclient 去请求传输是行不通的, feignclient 默认的序列化(json序列化)这关就过不了,我们还得借助 org.crazycake.shiro.serializer.ObjectSerializer 来实现序列化,以下是代码片断:

private RedisSerializer valueSerializer = new ObjectSerializer();
public SessionResult readSession(@RequestBody SessionReq req) {
    SessionResult res = new SessionResult<>();
    try {
        Session session = redisSessionDAO.readSession((Serializable)req.getSid());
        byte[] serialize = valueSerializer.serialize(session);
        String base64str = Base64.getEncoder().encodeToString(serialize);
        res.setData(base64str);
    } catch (UnknownSessionException e) {
        // do sth...
    } catch (SerializationException e) {
        // do sth...
    }


    log.debug("auth 返回:::::: {}", res);
    return res;
}

如果是用的 feignclient 的话,还需要对指定的 feignclient 做点 config 本文就不展开了。

能把 Session 的完整信息通过网络进行传输, 序列化的问题就解决掉了,当然这样做也有缺点比如效率问题等,最终我也不会让这种状态一直存在,会通过架构升级来解决这些问题。当前这个方案其实还有进一步优化空间,假如架构角度没有风险的话,可以直接从 redis 读出 byte[] 数组,再进行 base64 ,这点我们可以读下 RedisSessionDao 的源码看看它是怎么处理的,实际上 shiro 从 redis 中拿 session,也是用的 org.crazycake.shiro.serializer.ObjectSerializer 做的序列化和反序列化【哈哈笑的表情】:

protected Session doReadSession(Serializable sessionId) {
   if (sessionId == null) {
      logger.warn("session id is null");
      return null;
   }
   Session s = getSessionFromThreadLocal(sessionId);

   if (s != null) {
      return s;
   }

   logger.debug("read session from redis");
   try {
      s = (Session) valueSerializer.deserialize(redisManager.get(keySerializer.serialize(getRedisSessionKey(sessionId))));
      setSessionToThreadLocal(sessionId, s);
   } catch (SerializationException e) {
      logger.error("read session error. settionId=" + sessionId);
   }
   return s;
}

就标红的这一行,基本上已经说明我们的问题了。  valueSerializer 就是一个 org.crazycake.shiro.serializer.ObjectSerializer 。redisManager 得到的是 session 序列化后的 byte[] 数组。

稍微贴下它 ObjectSerializer  的源码一探究竟何方神圣:

@Override
public byte[] serialize(Object object) throws SerializationException {
    byte[] result = new byte[0];

    if (object == null) {
        return result;
    }
    ByteArrayOutputStream byteStream = new ByteArrayOutputStream(BYTE_ARRAY_OUTPUT_STREAM_SIZE);
    if (!(object instanceof Serializable)) {
        throw new SerializationException("requires a Serializable payload "
                + "but received an object of type [" + object.getClass().getName() + "]");
    }
    try {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteStream);
        objectOutputStream.writeObject(object);
        objectOutputStream.flush();
        result =  byteStream.toByteArray();
    } catch (IOException e) {
        throw new SerializationException("serialize error, object=" + object, e);
    }

    return result;
}

@Override
public Object deserialize(byte[] bytes) throws SerializationException {
    Object result = null;

    if (bytes == null || bytes.length == 0) {
        return result;
    }

    try {
        ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteStream);
        result = objectInputStream.readObject();
    } catch (IOException e) {
        throw new SerializationException("deserialize error", e);
    } catch (ClassNotFoundException e) {
        throw new SerializationException("deserialize error", e);
    }

    return result;
}

大呼上当,也没什么神奇的也就是 ByteArrayOutputStream + ObjectOutputStream 而已。。

可能也有人会说为什么不壮士断碗,直接把权限弄到 gateway 里面去。本文就不讨论了,有条件能一步到位的还是鼓励一步到位。

标签:return,SimpleSession,transient,private,session,new,序列化,shiro
来源: https://blog.csdn.net/weixin_38989369/article/details/121436265

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有