日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

Spring Boot分布式系统实践【扩展1】shiro+redis实现session共享、simplesession反序列化失败的问题定位及反思改进...

發布時間:2023/12/20 windows 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring Boot分布式系统实践【扩展1】shiro+redis实现session共享、simplesession反序列化失败的问题定位及反思改进... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

調試之前請先關閉Favicon配置

spring:favicon:enabled: false

不然會發現有2個請求(如果用nginx+ 瀏覽器調試的話)

序列化工具類【fastjson版本1.2.37】

```public class FastJson2JsonRedisSerializer implements RedisSerializer {

????public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

????private Class clazz;

????public FastJson2JsonRedisSerializer(Class clazz) {
????????super();
????????this.clazz = clazz;
????}

????@Override
????public byte[] serialize(T t) throws SerializationException {
????????if (t == null) {
????????????return new byte[0];
????????}
????????return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
????}

????@Override
????public T deserialize(byte[] bytes) throws SerializationException {
????????if (bytes == null || bytes.length <= 0) {
????????????return null;
????????}
????????String str = new String(bytes, DEFAULT_CHARSET);

????????return (T) JSON.parseObject(str, clazz);

????}
}

`org.apache.shiro.session.mgt.SimpleSession存儲到redis中會發現已經丟失了所有屬性`![Image [1].png](https://upload-images.jianshu.io/upload_images/231328-ab9c9ca3c2b43710.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)#### 查看SimpleSession源碼:

public class SimpleSession implements ValidatingSession, Serializable {

????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;
/* Serializes this object to the specified output stream for JDK Serialization.

  • @param out output stream used for Object serialization.
  • @throws IOException if any of this object's fields cannot be written to the stream.
  • @since 1.0
    */
    private void writeObject(ObjectOutputStream out) throws IOException {
    ????out.defaultWriteObject();
    ????short alteredFieldsBitMask = getAlteredFieldsBitMask();
    ????out.writeShort(alteredFieldsBitMask);
    ????if (id != null) {
    ????????out.writeObject(id);
    ????}
    ????if (startTimestamp != null) {
    ????????out.writeObject(startTimestamp);
    ????}
    ????if (stopTimestamp != null) {
    ????????out.writeObject(stopTimestamp);
    ????}
    ????if (lastAccessTime != null) {
    ????????out.writeObject(lastAccessTime);
    ????}
    ????if (timeout != 0l) {
    ????????out.writeLong(timeout);
    ????}
    ????if (expired) {
    ????????out.writeBoolean(expired);
    ????}
    ????if (host != null) {
    ????????out.writeUTF(host);
    ????}
    ????if (!CollectionUtils.isEmpty(attributes)) {
    ????????out.writeObject(attributes);
    ????}
    }

/*

  • Reconstitutes this object based on the specified InputStream for JDK Serialization.
  • @param in the input stream to use for reading data to populate this object.
  • @throws IOException????????????if the input stream cannot be used.
  • @throws ClassNotFoundException if a required class needed for instantiation is not available in the present JVM
  • @since 1.0
    */
    @SuppressWarnings({"unchecked"})
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
***發現transient修飾,所以Fastjson不會對這些transient屬性進行持久化,所以有了方案二,重寫可以json序列化的對象 同時發現有writeObject()方法寫著“ Serializes this object to the specified output stream for JDK Serialization.”, 所以有了方案一,修改序列化工具( 默認使用JdkSerializationRedisSerializer,這個序列化模式會將value序列化成字節碼) 問題我們就好對癥下藥了*** ## 方案一:修改序列化工具類 (`這個方式其實有問題`)

public class FastJson2JsonRedisSerializer implements RedisSerializer {
????private Class clazz;
????public FastJson2JsonRedisSerializer(Class clazz) {
????????super();
????????this.clazz = clazz;
????}
????@Override
????public byte[] serialize(T t) {
????????return ObjectUtils.serialize(t);
????}
????@Override
????public T deserialize(byte[] bytes) {
????????return (T) ObjectUtils.unserialize(bytes);
????}
}

### ObjectUtils的方法如下:

/**

  • 序列化對象
  • @param object
  • @return
    */
    public static byte[] serialize(Object object) {
    ???ObjectOutputStream oos = null;
    ???ByteArrayOutputStream baos = null;
    ???try {
    ??????if (object != null){
    ?????????baos = new ByteArrayOutputStream();
    ?????????oos = new ObjectOutputStream(baos);
    ?????????oos.writeObject(object);
    ?????????return baos.toByteArray();
    ??????}
    ???} catch (Exception e) {
    ??????e.printStackTrace();
    ???}
    ???return null;
    }

/**

  • 反序列化對象
  • @param bytes
  • @return
    */
    public static Object unserialize(byte[] bytes) {
    ???ByteArrayInputStream bais = null;
    ???try {
    ??????if (bytes != null && bytes.length > 0){
    ?????????bais = new ByteArrayInputStream(bytes);
    ?????????ObjectInputStream ois = new ObjectInputStream(bais);
    ?????????return ois.readObject();
    ??????}
    ???} catch (Exception e) {
    ??????e.printStackTrace();
    ???}
    ???return null;
    }
***`此方案會嚴重依賴對象class,如果反序列化時class對象不存在則會報錯 修改為: JdkSerializationRedisSerializer `***![Image [2].png](https://upload-images.jianshu.io/upload_images/231328-900964ebbd4757e2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/900)### 方案二:***繼承SimpleSession并重寫 讓相關的字段可以被序列化(不被transient修飾) 重寫之后一定要重寫SessionManager里的方法***

@Override
protected Session newSessionInstance(SessionContext context) {
SimpleSession session = new MyRedisSession(context.getHost());
// session.setId(IdGen.uuid());
session.setTimeout(SessionUtils.SESSION_TIME);
return session;
}

#### 由方案二引發的另一個問題就是: **`在微服務開發過程中,為了使用方便經常會將頻繁訪問的信息如用戶、權限等放置到SESSION中,便于服務訪問,而且,微服務間為了共享SESSION,通常會使用Redis共享存儲。但是這樣就會有一個問題,在封裝Request對象時會將當前SESSION中所有屬性對象反序列化,反序列化都成功以后,將SESSION對象生成。如果有一個微服務將本地的自定義Bean對象放置到SESSION中,則其他微服務都將出現反序列化失敗,請求異常,服務將不能夠使用了,這是一個災難性問題。`** ##### 以下是為了解決下面問題提出來的一種思路。 反序列化失敗在于Attribute中添加了復雜對象,由此推出以下解決方案:1. 將復雜對象的(即非基本類型的)Key進行toString轉換(轉換之后再MD5縮減字符串,或者用類名代替) 2. 將復雜對象的(即非基本類型的)Value進行JSON化(不使用不轉換的懶加載模式)`注意: 日期對象的處理(單獨處理)`

??/**
?????* 通過類型轉換,將String反序列化成對象
?????* @param key
?????* @param value
?????* @return
?????*/
????public Object getObjectValue(String key,String value){
????????if(key == null || value == null){
???????????return null;
????????}
????????String clz = key.replace(FLAG_STR,"");
????????try {
???????????Class aClass = Class.forName(clz);
???????????if(aClass.equals(Date.class)){
???????????????return DateUtils.parseDate(value);
???????????}
??????????return???JSONObject.parseObject(value,aClass);
????????} catch (ClassNotFoundException e) {
????????????e.printStackTrace();
????????}
//????????如果反序列化失敗就進行json化處理
????????return JSONObject.parseObject(value);
????}

```
經過如此處理可以在所有系統里共享緩存
唯一缺點就是太復雜了,可能引起其他系統的修改導致反序列化失敗(這個下次再討論或者實驗,因為有這么復雜的功夫,就可以考慮用JWT)

還有一種方案是將復雜對象放到redis中去,實行懶加載機制(不用的復雜對象,不從redis里獲取,暫未實現測試)

轉載于:https://www.cnblogs.com/Halburt/p/10552582.html

總結

以上是生活随笔為你收集整理的Spring Boot分布式系统实践【扩展1】shiro+redis实现session共享、simplesession反序列化失败的问题定位及反思改进...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。