spring boot+spring security认证authorities反序列化失败的错误,SimpleGrantedAuthority`cannot deserialize Object

报错信息


org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Cannot construct instance of `org.springframework.security.core.authority.SimpleGrantedAuthority` 
(although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (byte[])"{"@class":"com.xgj.application.domain.LoginUser","user":{"@class":"com.xgj.application.entity.User","id":2,"userName":"user1","nickName":"One",
"password":"$2a$10$zu9.95bseAFDpVGfjTv/XejhEGJhXdA8EV9mZaGpfHX6THQGYOPNy","status":"0","email":"user1@example.com","phoneNumber":"12345678901","sex":"1","avatar":"avatar_url1","userType":"1","createBy":null,"createTime":["java.util.Date",1723900660000],"updateBy":null,"updateTime":["java.util.Date",1723900660000],"delFlag":0},
"permissions":["java.util.Arr"
[truncated 595 bytes]; line: 1, column: 670] (through reference chain: com.xgj.application.domain.LoginUser["authorities"]->java.util.ArrayList[0]); nested exception is 

实体类中所有的有返回值的方法都会将返回的值序列化,但是反序列化时是根据set方法来实现的,所以当实体类中有非get,set方法的方法有返回值时,反序列化时就会出错。
例如

public class User implements UserDetails {
    
    public String userid;
    public String Uusername;
    public String Ppassword;
    public String role;
    public String getUusername() {
        return Uusername;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public void setUusername(String uusername) {
        Uusername = uusername;
    }

    public String getPpassword() {
        return Ppassword;
    }

    public void setPpassword(String ppassword) {
        Ppassword = ppassword;
    }
    public String getUserid() {
        return userid;
    }

    public void setUserid(String userid) {
        this.userid = userid;
    }


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return Ppassword;
    }

    @Override
    public String getUsername() {
        return Uusername;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

在实体类user中只有userid,Uusername, Ppassword,role这四个属性都有get,set方法,但是其他方法有返回值,所以在序列化时,其他方法返回的值也会被序列化存在缓存中,此时不会出错,但是从缓存中反序列化时就因为有些值没有set方法,所以会出错。

解决方法一:
实体类中只放get,set方法或返回值为空的方法。

解决方法二:
但有时会碰到必须要有有返回值的其他方法,例如在整合security时自定义登录认证和权限认证时需要继承UserDetail,就必须有其他方法,这时候可以RedisTemplate的设置里加
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);具体如在下方
 

package com.xgj.application.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
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.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {

        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(connectionFactory);
        //序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        //om.activateDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);  过期方法,采用下面代替
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //String序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

}

解决方法三:就是在有返回值的非get,set方法中加@JsonIgnore 

package com.xgj.application.domain;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.xgj.application.entity.User;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @Auther: xgj
 * @Desc:
 * @Create: 2024/8/17
 * @Verson: 1.0
 */
@Data
@NoArgsConstructor
public class LoginUser implements UserDetails {


    private User user;
    
    private List<String> permissions;
    // 用户的权限列表,通常是GrantedAuthority的集合
    private Collection<? extends GrantedAuthority> authorities;

    // 实现UserDetails接口所需的方法
    @Override
    @JsonIgnore
    public Collection<? extends GrantedAuthority> getAuthorities() {
        if (CollectionUtil.isNotEmpty(authorities)) {
            return authorities;
        }
        List<SimpleGrantedAuthority> grantedAuthorities = new ArrayList<>();
        for (String permission : permissions) {
            SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(permission);
            grantedAuthorities.add(simpleGrantedAuthority);
        }
        authorities = grantedAuthorities;
        return authorities;
    }

 

    public LoginUser(User user, List<String> permissions) {
        this.user = user;
        this.permissions = permissions;
    }


  
    @Override
    public String getPassword() {
        return user.getPassword();
    }

    
    @Override
    public String getUsername() {
        return user.getUserName();
    }

    @JsonIgnore
    @Override
    public boolean isAccountNonExpired() {
        return true; // 假设status >= 0表示账户未过期
    }

    @JsonIgnore
    @Override
    public boolean isAccountNonLocked() {
        return true; // 假设账户总是未锁定的
    }

    @JsonIgnore
    @Override
    public boolean isCredentialsNonExpired() {
        return true; // 假设凭证总是未过期的
    }

    @JsonIgnore
    @Override
    public boolean isEnabled() {
        return true; // 假设status == 0表示账户启用
    }
}

附上加了@JsonIgnore注解后,Redis中存的值没有那些isEnabled,isAccountNonLocked等值。Redis存的值如下

{"@class":"com.xgj.application.domain.LoginUser","user":{"@class":"com.xgj.application.entity.User","id":2,"userName":"user1",
"nickName":"One","password":"$2a$10$zu9.95bseAFDpVGfjTv/XejhEGJhXdA8EV9mZaGpfHX6THQGYOPNy","status":"0","email":"user1@example.com","phoneNumber":"12345678901","sex":"1",
"avatar":"avatar_url1","userType":"1","createBy":null,"createTime":["java.util.Date",1723900660000],"updateBy":null,"updateTime":["java.util.Date",1723900660000],"delFlag":0},"permissions":["java.util.ArrayList",["ROLE_test","ROLE_admin","ROLE_supadmin"]]}

tips: 权限信息(authorities)不会存到缓存Redis中获取值后permissions时会set到authorities中去。从Redis获取到的值如下

(user=User(id=2, userName=user1, nickName=One, password=$2a$10$zu9.95bseAFDpVGfjTv/XejhEGJhXdA8EV9mZaGpfHX6THQGYOPNy, status=0, email=user1@example.com, phoneNumber=12345678901, sex=1, avatar=avatar_url1, userType=1, createBy=null, createTime=Sat Aug 17 21:17:40 CST 2024, updateBy=null, updateTime=Sat Aug 17 21:17:40 CST 2024, delFlag=0), permissions=[ROLE_test, ROLE_admin, ROLE_supadmin], authorities=[ROLE_test, ROLE_admin, ROLE_supadmin])

Spring Boot低版本可以试一下加如下注解。

        参考了很多博客总结出来的,感谢所有分享技术博客的朋友们。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值