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连接池中的某些连接已经失效导致的。要解决这个问题,可以尝试以下几种方法:
可以尝试将连接池的大小调大,以保证连接数足够满足应用的需求。在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
如果连接池的大小已经足够大,可以考虑检查redis服务器的配置,以确保它能够支持应用程序所需的连接数。可以尝试增加redis服务器的内存或调整一些其他配置参数。
如果服务器和redis服务器之间的网络环境不稳定或者延迟较高,也可能导致连接池中的连接失效。可以使用ping命令或其他网络测试工具检查网络的连通性和稳定性,并在必要时优化网络环境。
可能是jedis版本与redis版本不相适应,需要根据redis服务器的版本更新jedis的版本号。
【相关推荐】
毫无疑问,链接不上。
看下是host或者密码哪个不对。
有一点需要注意下,yml中的value不能有@等特殊字符,如果有,两端加上单引号可以解决问题。
spring-data-redis官网文档(很不错):
这个错误通常出现在使用连接池时,当用户试图操作一个已经被归还给连接池的对象时,会引发 "Invalidated object not currently part of this pool" 的异常。
造成这个错误的主要原因是在连接归还给池之后, 应用程序或其他线程执行了某些操作使得该连接被关闭。然后,当应用程序再次尝试从池中获取连接并使用该连接时,就会抛出这个异常。
为了解决这个问题,可以采取以下几个步骤:
确保连接池的配置正确
首先,需要检查连接池的配置是否正确。连接池的配置包括池的大小、最大等待时间和连接超时时间等。如果连接池的配置不当,可能导致连接过期或者无法获取连接,应该对其进行调整以符合实际需求。
确保正确地使用连接对象
确保连接对象仅在其有效期内使用,并及时关闭,以防止出现无用的连接占用连接池的资源。在使用完连接对象后,及时将其归还给连接池,以避免连接对象被关闭或过期。
处理异常情况
当连接池中的连接由于某些原因被关闭或者无效时,应该捕获这些异常并及时处理。对于连接池的关闭异常,应该销毁连接池并重新创建一个连接池对象,并创建新的连接。
总结来说,解决 "Invalidated object not currently part of this pool" 异常的关键是合理配置连接池、正确使用连接对象、及时归还连接,以及处理异常情况。只有在这些方面做好了相应的工作,才能有效地避免这个问题的发生