Springboot整合 shiro+redis报错JedisException: Could not return the broken resource to the pool

springboot整合shiro+redis 整合完成以后 功能是正常的 但是控制台每隔一段时间就报错

2023-08-30 16:38:36.959 WARN [commons-pool-evictor]redis.clients.jedis.JedisFactory.destroyObject:168 -Error while close
redis.clients.jedis.exceptions.JedisException: Could not return the broken resource to the pool
    at redis.clients.jedis.util.Pool.returnBrokenResourceObject(Pool.java:126)
    at redis.clients.jedis.util.Pool.returnBrokenResource(Pool.java:103)
    at redis.clients.jedis.JedisPool.returnResource(JedisPool.java:389)
    at redis.clients.jedis.JedisPool.returnResource(JedisPool.java:15)
    at redis.clients.jedis.Jedis.close(Jedis.java:4081)
    at redis.clients.jedis.JedisFactory.destroyObject(JedisFactory.java:166)
    at org.apache.commons.pool2.PooledObjectFactory.destroyObject(PooledObjectFactory.java:127)
    at org.apache.commons.pool2.impl.GenericObjectPool.destroy(GenericObjectPool.java:611)
    at org.apache.commons.pool2.impl.GenericObjectPool.evict(GenericObjectPool.java:729)
    at org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor.run(BaseGenericObjectPool.java:160)
    at org.apache.commons.pool2.impl.EvictionTimer$WeakRunner.run(EvictionTimer.java:113)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.runAndReset$$$capture(FutureTask.java:308)
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: Invalidated object not currently part of this pool
    at org.apache.commons.pool2.impl.GenericObjectPool.invalidateObject(GenericObjectPool.java:936)
    at org.apache.commons.pool2.impl.GenericObjectPool.invalidateObject(GenericObjectPool.java:914)
    at redis.clients.jedis.util.Pool.returnBrokenResourceObject(Pool.java:124)
    ... 18 common frames omitted
2023-08-30 16:38:36.961 WARN [commons-pool-evictor]redis.clients.jedis.JedisFactory.destroyObject:168 -Error while close
redis.clients.jedis.exceptions.JedisException: Could not return the broken resource to the pool
    at redis.clients.jedis.util.Pool.returnBrokenResourceObject(Pool.java:126)
    at redis.clients.jedis.util.Pool.returnBrokenResource(Pool.java:103)
    at redis.clients.jedis.JedisPool.returnResource(JedisPool.java:389)
    at redis.clients.jedis.JedisPool.returnResource(JedisPool.java:15)
    at redis.clients.jedis.Jedis.close(Jedis.java:4081)
    at redis.clients.jedis.JedisFactory.destroyObject(JedisFactory.java:166)
    at org.apache.commons.pool2.PooledObjectFactory.destroyObject(PooledObjectFactory.java:127)
    at org.apache.commons.pool2.impl.GenericObjectPool.destroy(GenericObjectPool.java:611)
    at org.apache.commons.pool2.impl.GenericObjectPool.evict(GenericObjectPool.java:729)
    at org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor.run(BaseGenericObjectPool.java:160)
    at org.apache.commons.pool2.impl.EvictionTimer$WeakRunner.run(EvictionTimer.java:113)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.runAndReset$$$capture(FutureTask.java:308)
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:748)

下面是我的配置文件

package com.kth.basicadmin.common.config.shiro;

import com.alibaba.druid.support.http.StatViewServlet;
import com.kth.basicadmin.common.utils.LoadPackageClasses;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.filter.DelegatingFilterProxy;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;



/**
 * @author 10412
 */
@Configuration
public class ShiroConfig implements EnvironmentAware{



    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new CustomShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager());
        // 没有登录的用户只能访问登录页面,设置
        factoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的链接
        factoryBean.setSuccessUrl("/index");
        // 未授权界面; ----这个配置了没卵用,具体原因想深入了解的可以自行百度
//        factoryBean.setUnauthorizedUrl("/unauth");

        // 权限控制map.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //配置不登录可以访问的资源,anon 表示资源都可以匿名访问
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/img/**", "anon");
        filterChainDefinitionMap.put("/auth/login", "anon");
        /**
         * 其他资源都需要认证  authc 表示需要认证才能进行访问 user表示配置记住我或认证通过可以访问的地址
         *如果开启限制同一账号登录,改为 .put("/**", "kickout,user");
         **/
        filterChainDefinitionMap.put("/*Controller/*", "user");
        filterChainDefinitionMap.put("/index", "authc");

        factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return factoryBean;
    }

    @Bean
    public MethodInvokingFactoryBean methodInvokingFactoryBean() {
        MethodInvokingFactoryBean bean = new MethodInvokingFactoryBean();
        bean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        bean.setArguments(securityManager());
        return bean;
    }



    @Bean(name = "rememberAuthFilter")
    public RememberAuthenticationFilter rememberAuthenticationFilter() {
        RememberAuthenticationFilter filter = new RememberAuthenticationFilter();
        filter.setUsernameParam("userName");
        return filter;
    }
    /**
     * cookie对象;会话Cookie模板 ,默认为: JSESSIONID 问题: 与SERVLET容器名冲突,重新定义为sid或rememberMe,自定义
     * @return
     */
    @Bean
    public SimpleCookie rememberMeCookie(){
        //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //setcookie的httponly属性如果设为true的话,会增加对xss防护的安全系数。它有以下特点:
        //setcookie()的第七个参数
        //设为true后,只能通过http访问,javascript无法访问
        //防止xss读取cookie
        simpleCookie.setHttpOnly(true);
        simpleCookie.setPath("/");
        //<!-- 记住我cookie生效时间30天 ,单位秒;-->
        simpleCookie.setMaxAge(2592000);
        return simpleCookie;
    }

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

    /**
     * FormAuthenticationFilter 过滤器 过滤记住我
     * @return
     */
    @Bean
    public FormAuthenticationFilter formAuthenticationFilter(){
        FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
        //对应前端的checkbox的name = rememberMe
        formAuthenticationFilter.setRememberMeParam("rememberMe");
        return formAuthenticationFilter;
    }

    @Bean
    public FilterRegistrationBean registrationBean() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
        //  该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
        filterRegistration.addInitParameter("targetFilterLifecycle", "true");
        // 可以自己灵活的定义很多,避免一些根本不需要被Shiro处理的请求被包含进来
        filterRegistration.addUrlPatterns("/*");
        filterRegistration.addUrlPatterns("/WEB-INF/jsp/*");
        filterRegistration.addUrlPatterns("/login");
        filterRegistration.addUrlPatterns("/index");
        filterRegistration.addUrlPatterns("/logout");
        return filterRegistration;
    }

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm.
        securityManager.setRealm(myShiroRealm());
        // 自定义缓存实现 使用redis
        securityManager.setCacheManager(cacheManager());
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    /**
     * 身份认证realm; (这个需要自己写,账号密码校验;权限等)
     *
     * @return
     */
    @Bean
    public CustomRealm myShiroRealm() {
        CustomRealm customRealm = new CustomRealm();
        return customRealm;
    }

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

    /**
     * 配置shiro redisManager
     * 使用的是shiro-redis开源插件
     *
     * @return
     */
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        Optional<String> hostOpt = Optional.ofNullable(environment.getProperty("spring.redis.host"));
        String host = hostOpt.orElse("localhost");
        Optional<String> portOpt = Optional.ofNullable(environment.getProperty("spring.redis.port"));
        int port = Integer.parseInt(portOpt.orElse("6379"));
        Optional<String> timeoutOpt = Optional.ofNullable(environment.getProperty("spring.redis.timeout"));
        int timeout = Integer.parseInt(timeoutOpt.orElse("2000"));
        Optional<String> expireOpt = Optional.ofNullable(environment.getProperty("spring.redis.expire"));
        int expire = Integer.parseInt(expireOpt.orElse("2000"));
        Optional<String> passwordOpt = Optional.ofNullable(environment.getProperty("spring.redis.password"));
        String password = passwordOpt.orElse("");
        redisManager.setHost(host);
        redisManager.setPort(port);
        // 配置缓存过期时间
        redisManager.setExpire(expire);
        redisManager.setTimeout(timeout);
        if(password!=null&&!"".equals(password)){
            redisManager.setPassword(password);
        }

        return redisManager;
    }

    /**
     * Session Manager
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        sessionManager.setGlobalSessionTimeout(Long.valueOf(environment.getProperty("core.sessionMaxTimeout")));
        return sessionManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        Optional<String> property = Optional.ofNullable(environment.getProperty("server.servlet.context-path"));
        String context = property.orElse("/sso");
        redisSessionDAO.setKeyPrefix("shiro:session:" + context.replace("/","") + ":");
        return redisSessionDAO;
    }




    /***
     * 授权所用配置
     *
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    /***
     * 使授权注解起作用不如不想配置可以在pom文件中加入
     * <dependency>
     *<groupId>org.springframework.boot</groupId>
     *<artifactId>spring-boot-starter-aop</artifactId>
     *</dependency>
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * Shiro生命周期处理器
     *
     */
    @Bean
    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    private Environment environment;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }



  @Bean
  public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() {
    ServletRegistrationBean<StatViewServlet> registrationBean =
        new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
    // IP白名单 (没有配置或者为空,则允许所有访问)
//    registrationBean.addInitParameter("allow", "127.0.0.1");
    // IP黑名单 (存在共同时,deny优先于allow)
//    registrationBean.addInitParameter("deny", "");
//    registrationBean.addInitParameter("loginUsername", "root");
//    registrationBean.addInitParameter("loginPassword", "kth54321");
//    registrationBean.addInitParameter("resetEnable", "false");
    return registrationBean;
  }


}

题主,这个问题我来替你解决,若有帮助,还望采纳,点击回答右侧采纳即可。


这个错误通常是由于Jedis连接池中的某些连接已经失效导致的。要解决这个问题,可以尝试以下几种方法:

  1. 增大连接池的大小

可以尝试将连接池的大小调大,以保证连接数足够满足应用的需求。在Spring Boot中,可以在application.yml或application.properties中配置相应的属性,如下所示:

spring.redis.jedis.pool.max-active=100
spring.redis.jedis.pool.max-idle=50
spring.redis.jedis.pool.min-idle=10
  1. 重新配置redis服务器

如果连接池的大小已经足够大,可以考虑检查redis服务器的配置,以确保它能够支持应用程序所需的连接数。可以尝试增加redis服务器的内存或调整一些其他配置参数。

  1. 检查网络环境

如果服务器和redis服务器之间的网络环境不稳定或者延迟较高,也可能导致连接池中的连接失效。可以使用ping命令或其他网络测试工具检查网络的连通性和稳定性,并在必要时优化网络环境。

  1. 更新jedis版本

可能是jedis版本与redis版本不相适应,需要根据redis服务器的版本更新jedis的版本号。

【相关推荐】




如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

这个错误通常出现在使用连接池时,当用户试图操作一个已经被归还给连接池的对象时,会引发 "Invalidated object not currently part of this pool" 的异常。

造成这个错误的主要原因是在连接归还给池之后, 应用程序或其他线程执行了某些操作使得该连接被关闭。然后,当应用程序再次尝试从池中获取连接并使用该连接时,就会抛出这个异常。

为了解决这个问题,可以采取以下几个步骤:

确保连接池的配置正确
首先,需要检查连接池的配置是否正确。连接池的配置包括池的大小、最大等待时间和连接超时时间等。如果连接池的配置不当,可能导致连接过期或者无法获取连接,应该对其进行调整以符合实际需求。

确保正确地使用连接对象
确保连接对象仅在其有效期内使用,并及时关闭,以防止出现无用的连接占用连接池的资源。在使用完连接对象后,及时将其归还给连接池,以避免连接对象被关闭或过期。

处理异常情况
当连接池中的连接由于某些原因被关闭或者无效时,应该捕获这些异常并及时处理。对于连接池的关闭异常,应该销毁连接池并重新创建一个连接池对象,并创建新的连接。

总结来说,解决 "Invalidated object not currently part of this pool" 异常的关键是合理配置连接池、正确使用连接对象、及时归还连接,以及处理异常情况。只有在这些方面做好了相应的工作,才能有效地避免这个问题的发生