Redis反序列化的一次问题

redis反序列化的一次问题

1. 问题描述

springboot+redis不少用,但是一直没遇到什么问题,直接代码拷贝上去就用了。这次结合spring-security,将自定义的spring-security的UserDetails接口的实现类SecurityUser,反序列化取出时报错。

b 复制代码
org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Cannot construct instance of `com.jankin.inoteadmin.security.entity.SecurityUser` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
	......
org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.deserialize(GenericJackson2JsonRedisSerializer.java:165)

2. 问题解读

我猜肯定有人骂我了,上面问题描述不是很清晰了吗 no Creators, like default constructor, exist 。我的回答是,该骂,大骂特骂~

作为一名不称职的代码搬砖工,我遇到问题时,第一是间是查看是否实现了Serializable接口。确认父接口UserDetails实现了该接口还不死心,然后在本类中添加该接口,保存运行,报错依旧。慌了,按道理没道理啊,之前项目也是直接这样引依赖,建个RedisConfig类就行了啊,然后就打开百度......

结果还百度不出......(估计是没有人连这种问题需要百度,所以搜不到)

好,言归正传,问题就如上述所说,没有默认够构造器,也就是无参构造器。

我依稀记得大学教我java的老师,说到构造器时,反复强调,无论你建多少个有参构造器,都必须带上无参构造器,毕业后其实一直遵守老师所说的,每次建造构造方法时,都会建无参构造方法,lombok的 @AllArgsConstructor 和 @NoArgsConstructor我一直都是连着用的。这次没用不是因为忘记了,而是SpringSecurity中自带的UserDetails实现类org.springframework.security.core.userdetails.User 本身就没带无参构造器,并且,其中的类属性还带上了final关键字,意思是在声明时或者构造方法中必须定义初始值。

好嘛,SpringSecurity默认的User都没问题,我自定义的SecurityUser仿照应该问题也不大。

然后就出这档子事儿了。

扯远了,总而言之啊,就是spring中redis的反序列化需要依赖无参构造器

3. 问题解决

那就加上无参构造器呗,SpringSecurity的自定义的SecurityUser还需要将对象属性的final关键字去掉。

java 复制代码
public class SecurityUser implements UserDetails{

    private static final long serialVersionUID = 1L;

    private String userId;
    private String password;
    private String username;
    private Set<GrantedAuthority> authorities;
    private boolean accountNonExpired;
    private boolean accountNonLocked;
    private boolean credentialsNonExpired;
    private boolean enabled;
    
    /**
     * 无参构造器
     */
    public SecurityUser(){}

    /**
     * Simple Param Constructor
     * @param userId 用户id
     * @param password  用户密码
     * @param username  用户名
     * @param authorities   权限列表
     */
    public SecurityUser(String userId, String username, String password, Set<GrantedAuthority> authorities) {
        this(userId,username,password,authorities,
                true,true,true,true);
    }

    /**
     * AllParamConstructor
     * @param userId 用户id
     * @param password  用户密码
     * @param username  用户名
     * @param authorities   权限列表
     * @param accountNonExpired 账号没过期
     * @param accountNonLocked  账号没被锁
     * @param credentialsNonExpired 凭证没过期
     * @param enabled   正常
     */
    public SecurityUser(String userId, String username, String password, Set<GrantedAuthority> authorities,
                        boolean accountNonExpired, boolean accountNonLocked,
                        boolean credentialsNonExpired, boolean enabled) {
        this.userId = userId;
        this.username = username;
        this.password = password;
        this.authorities = Collections.unmodifiableSet(authorities);
        this.accountNonExpired = accountNonExpired;
        this.accountNonLocked = accountNonLocked;
        this.credentialsNonExpired = credentialsNonExpired;
        this.enabled = enabled;
    }
    // other todo
}

一点点题外话

作者还没做过什么大型项目,单体应用上看,前后端分离的情况下SpringSecurity的认证和授权根本上就是两个Filter和AOP做注解鉴权就行,感觉没有必要用SpringSecurity,之前用的shiro,也用过sa-token,相比之下,SpringSecurity是真的复杂,前后端分离情况下,SpringSecurity的UserDetailsService也拉,不好做缓存认证。

说到SpringSecurity的认证,网上很多方案都是使用jwt做token认证标识,对于jwt做下线功能该怎么做呢,jwt一旦颁发就只能到期失效。这样token被盗用的话一点办法都没有。有人说在缓存或数据库中添加signature的加密串,或者用账户密码做加密串,这样下线时或者更改密码时修改加密串就行......这样做确实能解决jwt的失效问题,但是jwt的主要特点(优点)就是无状态,每次认证只需要依赖jwt本体就行,这样数据库或者缓存添加jwt的加密串,岂不是违背了原本jwt的初衷?这样和使用普通token,用token做key缓存用户登录状态信息有什么区别。

虽然言语略带偏激,但是确实是心中疑问,有朋友看到且有见解的话,还请指教一下。

相关推荐
底层玩家老张7 分钟前
从副本集到分片:百亿级文档数据的KES架构边界
数据库·分布式架构·分片·架构选型·文档数据库
ss2738 分钟前
AI时代首先数据库-PostgreSQL实战笔记 | 16 PostgreSQL子查询与嵌套查询(下)
数据库·笔记·postgresql
川石课堂软件测试23 分钟前
涨薪技术|0到1学会性能测试第8课-Analysis分析图
服务器·数据库·python·sql·jmeter·mysql·容器
SelectDB技术团队32 分钟前
云数仓成本结构拆解:五层计费模型与 Apache Doris 托管形态对照
大数据·数据库·elk·全文检索·snowflake
星辰云-32 分钟前
# 基于Spring Boot的德州特产扒鸡商城系统(毕设|源码|论文|演示视频)
spring boot·后端·课程设计
夕除41 分钟前
redis--ziplist
redis
星辰云-43 分钟前
基于Spring Boot的旅游景点票务系统的设计与实现(毕设项目,附完整源码)
spring boot·后端·课程设计
我命由我123451 小时前
SQL 检测脚本
java·数据库·sql·功能测试·单元测试·压力测试·可用性测试
Zenova EdgeOS1 小时前
工业网关数据全链路加密实战:从传输、静态数据到密钥轮
数据库·oracle·边缘计算·工业网关
太理摆烂哥1 小时前
MySQL表的约束
数据库·mysql