SpringBoot集成shiro-redis遇到的问题(已解决) 1C

在通过***(SysUser) SecurityUtils.getSubject().getPrincipal()*** 获取当前登录对象时,**SecurityUtils.getSubject().getPrincipal()**是正确的对象。
当强转时报错com.spring.model.system.SysUser cannot be cast to com.spring.model.system.SysUser。
求解本人猜测是redis对象序列化反序列化导致,因为shiro没有集成redis时是正确的,但是经过测试,不走shiro单独存储读取对象是没有问题的,这块不是太懂,求大神解答
下面是相关的配置代码
redis配置

@Configuration
@EnableCaching
@EnableRedisHttpSession
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public CacheManager cacheManager(RedisTemplate<?, ?> redisTemplate) {
        RedisCacheManager manager = new RedisCacheManager(redisTemplate);
        manager.setDefaultExpiration(3600);//设置默认过期时间
        return manager;
    }

    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }
}





shiro配置
@Configuration
public class ShiroConfig {
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.timeout}")
    private int timeout;
    @Value("${spring.redis.password}")
    private String password;


    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //拦截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/doLogin", "anon");
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/images/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/libs/**", "anon");
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        // 配置需要验证登录后访问的链接
        filterChainDefinitionMap.put("/**", "authc");
        // 从数据库获取
//        List<AdminMenu> list = systemService.selectAllMenu();
//
//        for (AdminMenu menu : list) {
//            filterChainDefinitionMap.put(menu.getMenuUrl(), "authc");
//        }
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }


    /**
     * 凭证匹配器
     * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了)
     *
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(1);//散列的次数
        return hashedCredentialsMatcher;
    }


    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        // 自定义缓存实现 使用redis
        securityManager.setCacheManager(cacheManager());
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager());
        //注入记住我管理器;
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }

    /**
     * 身份认证realm; (自定义,账号密码校验;权限等)
     *
     * @return
     */
    @Bean
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    /**
     * 配置shiro redisManager
     * <p>
     * 使用的是shiro-redis开源插件
     *
     * @return
     */
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        redisManager.setExpire(1800);// 配置缓存过期时间
        redisManager.setTimeout(timeout);
        redisManager.setPassword(password);
        return redisManager;
    }

    /**
     * cacheManager 缓存 redis实现
     * <p>
     * 使用的是shiro-redis开源插件
     *
     * @return
     */
    @Bean
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * <p>
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }

    /**
     * Session Manager
     * <p>
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }

    /**
     * cookie对象;
     *
     * @return
     */
    public SimpleCookie rememberMeCookie() {
        //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //<!-- 记住我cookie生效时间7天 ,单位秒;-->
        simpleCookie.setMaxAge(604800);
        return simpleCookie;
    }

    /**
     * cookie管理对象;记住我功能
     *
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));
        return cookieRememberMeManager;
    }

    /**
     * 开启shiro aop注解支持.
     * 使用代理方式;所以需要开启代码支持;
     *
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
} 

去除spring-boot-devtools热部署jar包即可,具体原因不详

11个回答

是不是使用了springboot的热部署,如果是的话去除热部署就可以了

找到完美的解决方法了

不让用 devtools 会浪费很多的时间

解决方法

我找到一种解决方案, 老规矩先说方案
/resource/META-INF 目录下(没有就创建) 添加文件 spring-devtools.propertis
加入代码

restart.include.mapper=/mapper-[\\w-\\.]+jar
restart.include.pagehelper=/pagehelper-[\\w-\\.]+jar
# 因为我项目中引用了 org.crazycake:shiro-redis ,所以要引用下面这个配置
restart.include.shiro=/shiro-[\\w-\\.]+jar

加入后就见证了奇迹

代码解释

上面三行代码都是, 添加 jar 包到 restart 类加载器中 = 后面是具体的 jar 包名称, 或正则表达式

原因

DevTools 默认会对 IDE 中引入的所有项目使用 restart 类加载器, 对引入的 jar 包使用 base 类加载器, 因此要保证热部署时使用的类加载器一致就好了

u013197388
lixian13149999 回复z1729734271: 这个是单独的, 不是项目中的 yml 文件, 注意看目录
10 个月之前 回复
z1729734271
time倾听下 yml文件如何处理你的这种方式呢
接近 2 年之前 回复

兄弟,我遇到一个类似的问题,但又不完全一样,去掉热部署包还是不行。我使用的是spring boot +shiro+cas+redis的解决方案,我发现加了redis后,不能重定向到我之前的URL,跟踪代码,发现在WebUtils的getSavedRequest方法获取不到SavedRequest对象,导致没法重定向到原来的页面。我跟踪saveRequest方是保存成功的。
public static SavedRequest getSavedRequest(ServletRequest request) {
SavedRequest savedRequest = null;
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession(false);
if (session != null) {

     savedRequest = (SavedRequest) session.getAttribute(SAVED_REQUEST_KEY);这句话获取不到对象。
    }
    return savedRequest;
}
    如果我恢复为Ehcache一切又正常了。。。实在搞不懂是哪里出问题了。。如果有人遇到又解决了请给我发邮件39530426@qq.com,
finly203
finly 我自己解决了。发现是设置session超时的时候设置成1.8秒。。所以在登陆后之前的session失效了。
接近 2 年之前 回复

果然是热部署问题,用了spring-boot-devtools 热部署之后才导致类型无法转换,真是坑啊

select_delete
select_delete 完美解决问题,不过原因是啥
5 个月之前 回复
whh18254122507
丿明月丶夜 换个热部署工具吧
大约 2 年之前 回复
cong8377
cong8377 我也是遇到这问题,还真是热部署的问题,难道添加了热部署依赖包就会出现这问题
大约 2 年之前 回复

兄弟,我最近也遇到相同的问题。集成Redis,单独给securityManager设置cacheManager或者sessionManager都是可以的;
或者给cacheManager设置为ehcache,sessionManager设置为Redis也是可以的,唯独cacheManager和sessionManager都采用Redis的时候就会
在SecurityUtils.getSubject().getPrincipal()进行对象强转的时候报错,也怀疑过序列化的问题,
但是单独设置cacheManager为Redis的时候又可以转换,自己也尝试过去直接去redis取缓存,发现是可以取出来的,又感觉序列化没问题。
不知道你解决了没有?

qq1023544696
徐新龙 去除spring-boot-devtools热部署jar包,具体原因不详
接近 2 年之前 回复

**spring-boot-devtools **这个插件不知道怎么回事,有冲突

遇见相同的问题,反反复复弄了一晌才找到,确实是dev插件的问题,原因是二者所使用的类加载器不同,看这个https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-devtools.html#using-boot-devtools-customizing-classload

qianchenglenger
i_wooden By default, any open project in your IDE will be loaded using the “restart” classloader, and any regular .jar file will be loaded using the “base” classloader.
接近 2 年之前 回复

我也遇到相同的问题,只能去掉去掉插件了不知道有没有别的方法处理,求解答

我使用笨方法解决了,用反射获取属性,自己写了一个属性;
/**
* 用于redis session 使用了 spring devtools 导致的类型转换异常
* @param redisObj
* @return
*/
public static SysUserEntity convertObjToEntity(Object redisObj) {
SysUserEntity sysUserEntity = new SysUserEntity();
sysUserEntity.setUserId(NumberUtils.toLong(ReflectUtils.getFieldValue(redisObj, SysUserEntity.FIELD_USERID)+"",0));
sysUserEntity.setUsername(ReflectUtils.getFieldValue(redisObj, SysUserEntity.FIELD_USERNAME)+"");
sysUserEntity.setPassword(ReflectUtils.getFieldValue(redisObj, SysUserEntity.FIELD_PASSWORD)+"");
sysUserEntity.setEmail(ReflectUtils.getFieldValue(redisObj, SysUserEntity.FIELD_EMAIL)+"");
sysUserEntity.setMobile(ReflectUtils.getFieldValue(redisObj, SysUserEntity.FIELD_MOBILE)+"");
sysUserEntity.setStatus(NumberUtils.toInt(ReflectUtils.getFieldValue(redisObj, SysUserEntity.FIELD_STATUS)+"",0));
sysUserEntity.setCreateUserId(NumberUtils.toLong(ReflectUtils.getFieldValue(redisObj, SysUserEntity.FIELD_CREATEUSERID)+"",0));
Object dateObj = ReflectUtils.getFieldValue(redisObj, SysUserEntity.FIELD_CREATETIME);
sysUserEntity.setCreateTime(dateObj != null ? (Date) dateObj : null);
return sysUserEntity;
}

try {
        user = (SysUserEntity)principals.getPrimaryPrincipal();
    } catch (Exception e) {
        user = SysUserEntity.convertObjToEntity(principals.getPrimaryPrincipal());
    }


    try {
        user = (SysUserEntity)principals.getPrimaryPrincipal();
    } catch (Exception e) {
        user = SysUserEntity.convertObjToEntity(principals.getPrimaryPrincipal());
    }

您好,您配置的RedisConfig 也没用到啊?您这也没使用json序列化格式进行存储罢?

共11条数据 1 尾页
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!