qq1023544696
徐新龙
采纳率0%
2017-08-24 07:30 阅读 22.4k

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

1

在通过***(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条回答 默认 最新

  • u013197388 DanielLi_ 2018-01-05 06:24

    找到完美的解决方法了

    不让用 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 类加载器, 因此要保证热部署时使用的类加载器一致就好了

    点赞 6 评论 复制链接分享
  • czbczb1234 czbczb1234 2017-09-07 08:26

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

    点赞 3 评论 复制链接分享
  • finly203 finly 2017-12-05 11:54

    兄弟,我遇到一个类似的问题,但又不完全一样,去掉热部署包还是不行。我使用的是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,
    
    点赞 2 评论 复制链接分享
  • whh18254122507 丿明月丶夜 2017-09-09 07:59

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

    点赞 1 评论 复制链接分享
  • zxm94w zxm94w 2017-08-28 14:42

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

    点赞 评论 复制链接分享
  • weixin_39351426 weixin_39351426 2017-11-06 03:43

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

    点赞 评论 复制链接分享
  • qianchenglenger i_wooden 2017-11-20 15:46

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

    点赞 评论 复制链接分享
  • ab601026460 仰望明天 2017-12-17 12:18

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

    点赞 评论 复制链接分享
  • ab601026460 仰望明天 2017-12-17 13:21

    我使用笨方法解决了,用反射获取属性,自己写了一个属性;
    /**
    * 用于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());
        }
    
    点赞 评论 复制链接分享
  • K1215400017 樱木花道VS康 2018-07-07 12:27

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

    点赞 评论 复制链接分享
  • m0_38044453 m0_38044453 2019-05-24 17:33

    我配置了sessionmanager后,为什么启动总是报错

    package com.fly.feasy.shiro;
    
    import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
    import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    import org.apache.shiro.session.mgt.SessionManager;
    import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.CookieRememberMeManager;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.shiro.web.servlet.SimpleCookie;
    import org.crazycake.shiro.RedisCacheManager;
    import org.crazycake.shiro.RedisManager;
    import org.crazycake.shiro.RedisSessionDAO;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.apache.shiro.mgt.SecurityManager;
    
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * shiro 的配置类.
     */
    @Configuration
    public class ShiroConfig {
    
        /**
         * 一、在认证中:
         *    1.1,将加密算法定义好后扔到 MyShiroRealm中 也就是自己定义的realm中
         *    1.2,将MyShiroRealm定义后扔到SecurityManager中。
         *    1.3,后期用到session什么的,都被SecurityManager管理
         *
         * @return
         */
    
    
        /**
         * 二、配置session(用Redis存储)
         *    2.1 需要配置session,就需要将sessionManager配置在SecurityManager中。
         *    2.2 sessionManager需要交给Redis来管理,所以定义了RedisSessionDAO
         *    2.3 RedisSessionDAO中需要配置Redis的信息,所以定义RedisManager
         *
         * @return
         */
    
        @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;
    
    
        /**
         * 自定义shiro过滤器.
         * @param securityManager securityManager
         * @return ShiroFilterFactoryBean
         */
        @Bean(name = "shiroFilter")
        public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            // 必须设置secrityMananger
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            // 设置登录地址,配置shiro默认登录界面地址,前后端分离中登录界面跳转应由前端路由控制,后台仅返回json数据
            shiroFilterFactoryBean.setLoginUrl("/login");
            shiroFilterFactoryBean.setSuccessUrl("/main.html");
            // 设置拦截器
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
    
            // 设置静态资源(静态资源要设置在最前面)
            filterChainDefinitionMap.put("/layui-v2.4.5/**","anon");
            filterChainDefinitionMap.put("/css/**","anon");
            filterChainDefinitionMap.put("/js/**","anon");
    
    
            // authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问;登录,登出
            filterChainDefinitionMap.put("/login", "anon");
            filterChainDefinitionMap.put("/logout", "logout");
    
            // 设置静态资源
            filterChainDefinitionMap.put("/css/**","anon");
    
    //        filterChainDefinitionMap.put("/user/**", "authc");
            //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
            filterChainDefinitionMap.put("/**", "authc");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        }
    
        /**
         * 设置加密方式和加密次数.
         * 凭证匹配器.
         * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了).
         * @return HashedCredentialsMatcher
         */
        @Bean
        public HashedCredentialsMatcher hashedCredentialsMatcher() {
            HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
            // 加密的方式,散列算法:这里使用MD5算法;
            hashedCredentialsMatcher.setHashAlgorithmName("MD5");
            // 加密的次数,散列的次数,比如散列两次,相当于 md5(md5(""));
            hashedCredentialsMatcher.setHashIterations(2);
            hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
            return hashedCredentialsMatcher;
        }
    
        /**
         * 注入自定义Realm.
         * @return CustomRealm
         */
        @Bean
        public CustomRealm customRealm() {
            CustomRealm customRealm = new CustomRealm();
            // 把上面自己定义的凭证匹配器加入,不然token里面的密码不会进行加密
            customRealm.setCredentialsMatcher(hashedCredentialsMatcher());
            return customRealm;
        }
    
        //------------------------- redis-session ----------------------
    
        /**
         * 配置shiro redisManager
         * <p>
         * 使用的是shiro-redis开源插件
         *
         * @return
         */
        public RedisManager redisManager() {
            RedisManager redisManager = new RedisManager();
            redisManager.setHost(host);
            redisManager.setPort(port);
            redisManager.setTimeout(timeout);
            redisManager.setPassword(password);
            return redisManager;
        }
    
        /**
         * cacheManager 缓存 redis实现
         * <p>
         * 使用的是shiro-redis开源插件
         *
         * @return
         */
        public RedisCacheManager redisCacheManager(){
            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());
            redisSessionDAO.setExpire(1800);
            return redisSessionDAO;
        }
    
        /**
         * Session Manager
         * <p>
         * 使用的是shiro-redis开源插件
         */
        @Bean
        public SessionManager sessionManager() {
            MySessionManager mySessionManager = new MySessionManager();
            mySessionManager.setSessionDAO(redisSessionDAO());
            return mySessionManager;
        }
    
        // -------------------- cookie 记住我功能 --------------------------
    
        /**
         * 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;
        }
    
        /**
         * 把Realm注入到SecurityManager中.
         * @return SecurityManager
         */
        @Bean
        public SecurityManager securityManager() {
            DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
            defaultSecurityManager.setRealm(customRealm());
            // 自定义缓存实现 使用redis
            defaultSecurityManager.setCacheManager(redisCacheManager());
            // 自定义session管理 使用redis
            defaultSecurityManager.setSessionManager(sessionManager());
            //注入记住我管理器;
            defaultSecurityManager.setRememberMeManager(rememberMeManager());
            return defaultSecurityManager;
        }
    
        /**
         * 开启shiro aop注解支持.
         * 使用代理方式;所以需要开启代码支持;
         *
         * @param securityManager
         * @return
         */
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
            authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
            return authorizationAttributeSourceAdvisor;
        }
    
    }
    

    截图
    图片说明

    点赞 评论 复制链接分享

相关推荐