shiro继承redis序列化失败-----坑

shiro继承redis进行session的管理:

package com.maobc.common.core.redis;

import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;


@Configuration
@EnableCaching
@Data
public class RedisConfiguration extends CachingConfigurerSupport {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

   
    /**
     * 配置redis 连接
     */
    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
        // 1.创建 redisTemplate 模版
        RedisTemplate template = new RedisTemplate<>();
        // 2.关联 redisConnectionFactory
        template.setConnectionFactory(factory);
        // 3.创建 自定义序列化类
        MyRedisSerializer myRedisSerializer = new MyRedisSerializer();
        // 7.设置 value 的转化格式和 key 的转化格式 默认使用的是JdkSerializationRedisSerializer
        template.setValueSerializer(myRedisSerializer);
        template.setHashValueSerializer(myRedisSerializer);
        // 设置键(key)的序列化采用StringRedisSerializer。
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setDefaultSerializer(myRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    @ConditionalOnMissingBean(StringRedisTemplate.class)
    public RedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
        return new StringRedisTemplate(factory);
    }
}

    默认使用JdkSerializationRedisSerializer,这个序列化模式会将value序列化成字节码,这样缓存shiro的session就没有什么问题,当是redis数据库的数据将是字节码,不方便观察。

   如果改用GenericJackson2JsonRedisSerializer或者Jackson2JsonRedisSerializer,项目启动运行没有任何问题,但是在在访问过程中,突然性的报错:

      com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "valid" (class org.apache.shiro.session.mgt.SimpleSession), not marked as ignorable (10 known properties: "attributeKeys", "timeout", "startTimestamp", "expired", "lastAccessTime", "host", "id", "stopTimestamp", "attributes", "attributesLazy"])

这是由于是反序列化报错的原因。无法反序列化接口的动态代理类,原因应该是动态代理类没有缺省构造函数。

处理方式:     

  1. 解决session序列化问题方法也很简单,setValueSerializer不配置就可以了。
    如果想要redis数据库的数据为json字符串,那么可以在其他用到缓存的地方使用StringRedisTemplate,或者再定义一个template。

    2. 自定义序列化和反序列化方法。

MyRedisSerializer 序列化成字节数组
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

import java.io.*;

/**
 * 重写序列化 序列化为字节码
 * zhw
 */
public class MyRedisSerializer implements RedisSerializer {


    @Override
    public byte[] serialize(Object o) throws SerializationException {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream objOut;
        try {
            objOut = new ObjectOutputStream(byteOut);
            objOut.writeObject(o);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return byteOut.toByteArray();
    }

    @Override
    public Object deserialize(byte[] bytes) throws SerializationException {
        if(bytes == null) return null;
        ByteArrayInputStream byteIn = new ByteArrayInputStream(bytes);
        ObjectInputStream objIn;
        Object obj;
        try {
            objIn = new ObjectInputStream(byteIn);
            obj =objIn.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
        return obj;
    }

}
FastJsonRedisSerializer:序列化成json字符串
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

import java.nio.charset.Charset;

/**
 * 重写序列化 序列化为json字符串
 * zhw
 */
public class FastJsonRedisSerializer implements RedisSerializer {


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

    private Class clazz;

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

    @Override
    public byte[] serialize(Object o) throws SerializationException {
        if (o == null) {
            return new byte[0];
        }
        return JSON.toJSONString(o, 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 JSON.parseObject(str, clazz);
    }
}

      多个项目彼此之间需使用同一个redis,一个项目往redis存入数据,另一个项目从redis中获取数据,如果使用的不是同一个redis的序列化方式,这样导致,另一个项目是无法从redis中获取到该key所对应的数据。

你可能感兴趣的:(错误总结)