外观
记一次UnavailableSecurityManagerException异常
问题背景
org.apache.shiro.UnavailableSecurityManagerException: No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton. This is an invalid application configuration.
堆栈异常如上:
解决思路
这个报错的原因花了一段时间查找,有如下几种可能:
org.apache.shiro.UnavailableSecurityManagerException
异常通常表示尝试获取 SecurityManager
时失败了,因为 SecurityManager
没有被正确地初始化或配置。这个异常可能由以下几个原因引起:
- ShiroFilterFactoryBean 配置问题:如果你在使用 Spring 集成 Shiro 时遇到了这个问题,可能是因为
ShiroFilterFactoryBean
没有被正确配置。确保你已经将ShiroFilterFactoryBean
设置为一个@Bean
,并在其内部设置了SecurityManager
。例如:
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 其他配置...
return shiroFilterFactoryBean;
}
- DelegatingFilterProxy 配置问题:在 Spring Boot 应用中,如果你使用了
DelegatingFilterProxy
来代理 Shiro 的过滤器,确保你已经正确配置了这个代理。例如:
@Bean
public FilterRegistrationBean delegatingFilterProxy() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
DelegatingFilterProxy proxy = new DelegatingFilterProxy();
proxy.setTargetFilterLifecycle(true);
proxy.setTargetBeanName("shiroFilter"); // 注意这里的 bean 名称要与 shiroFilterFactoryBean 的名称一致
filterRegistrationBean.setFilter(proxy);
return filterRegistrationBean;
}
SecurityManager 未注入:确保
SecurityManager
已经被注入到你的应用上下文中,并且可以通过SecurityUtils
获取到。如果没有注入,你需要配置一个SecurityManager
的 bean,并确保它被ShiroFilterFactoryBean
使用。Filter 执行顺序问题:如果你定义了自定义的过滤器,并且这些过滤器在 Shiro 的
ShiroFilterFactoryBean
之前执行,可能会导致SecurityManager
未被正确设置。确保自定义过滤器是在 Shiro 过滤器之后执行的。Spring Boot 集成问题:如果你在使用 Spring Boot 集成 Shiro,可能需要额外注意配置的细节。例如,确保你的
ShiroFilterFactoryBean
bean 名称与DelegatingFilterProxy
中指定的名称一致。确保 Shiro 配置类被 Spring 管理:如果你的 Shiro 配置类没有被 Spring 管理,那么
SecurityManager
可能不会被正确初始化。确保你的配置类上有@Configuration
注解。
如果问题依然存在,建议检查 Shiro 配置类中的所有 @Bean
方法,确保它们都正确配置,并且没有遗漏任何必要的设置。同时,检查应用程序的日志,查看是否有其他相关的错误信息,这可能会提供更多关于问题的线索。
也看看有没有配漏@Bean疏忽导致。
解决方案
最终找到一个方案解决:
DelegatingFilterProxy
是 Spring 框架提供的一个非常有用的类,它的作用是将 Spring 容器管理的 Filter 代理为一个 Servlet Filter,这样就可以利用 Spring 的依赖注入和生命周期管理功能。这个代理类允许 Filter 实例享受 Spring 的依赖注入,并且可以通过 Spring 来管理 Filter 的生命周期 。
在 Spring Boot 应用中,如果你使用了 Filter
,并且希望这些 Filter
能够注入 Spring 容器中的其他组件,你可以通过 DelegatingFilterProxy
来实现。这样,你就可以在 Filter
中注入业务工具类或其他 Spring 管理的 Bean,而不会出现注入为 null
的问题 。
关于 ShiroFilterFactoryBean
的配置,它是用来配置 Shiro 过滤器链的入口点。通过这个 Bean,你可以设置安全管理器、登录 URL、权限不足时的跳转 URL 等。同时,你还可以自定义过滤器,并将其加入到 Shiro 的过滤器链中 。
在使用 Shiro 时,如果你遇到了 No SecurityManager accessible
这样的异常,通常是因为 Shiro 没有正确地获取到 SecurityManager
实例。这可能是因为 SecurityManager
没有被注册为 Spring 容器中的一个 Bean,或者 ShiroFilterFactoryBean
没有正确地设置 SecurityManager
属性。确保你在配置 Shiro 时,已经定义了一个 SecurityManager
的 Bean,并且通过 shiroFilterFactoryBean.setSecurityManager(securityManager())
方法将其设置给了 ShiroFilterFactoryBean
。
最后,确保你的自定义过滤器没有被 Spring 容器重复管理。如果你的自定义过滤器被声明为一个 Spring Bean,并且也通过 FilterRegistrationBean
注册到了 Spring 容器中,那么它可能会被执行两次。为了避免这个问题,你可以选择不让 Spring 管理你的自定义过滤器,而是直接在 ShiroFilterFactoryBean
中通过 new
关键字创建实例 。
相关问题
为什么使用 DelegatingFilterProxy 而不是直接使用 Filter?
DelegatingFilterProxy
是 Spring 提供的一个强大的工具,它允许你将 Spring 容器管理的过滤器(Filter)代理为一个 Servlet Filter。使用 DelegatingFilterProxy
而不是直接使用 Filter 主要有以下几个原因:
依赖注入: 使用
DelegatingFilterProxy
可以让你的过滤器享受到 Spring 的依赖注入功能。这意味着你可以在过滤器中注入其他 Spring 管理的 Bean,例如服务(Service)、数据访问对象(Repository)等。这在直接使用 Filter 时是无法做到的,因为 Filter 不是由 Spring 管理的。生命周期管理: Spring 管理的 Bean 有完整的生命周期管理,包括初始化和销毁回调。当你的过滤器作为 Spring Bean 时,你可以使用
@PostConstruct
和@PreDestroy
注解来执行初始化和清理工作。这有助于管理资源,如数据库连接或文件句柄。集成 Spring Security: 如果你的应用使用 Spring Security,
DelegatingFilterProxy
是集成 Spring Security 过滤器链的推荐方式。Spring Security 需要能够注入依赖项并管理生命周期,而DelegatingFilterProxy
使得这一点成为可能。一致的配置方式: 使用
DelegatingFilterProxy
可以保持你的配置方式一致性。在 Spring 应用中,几乎所有的组件都是通过 Spring 容器来管理的。通过使用DelegatingFilterProxy
,你的过滤器可以像其他组件一样被声明和配置。避免 Filter 链问题: 直接在 web.xml 中定义 Filter 可能会导致 Filter 链的问题,特别是当与其他框架(如 Spring Security、Shiro 等)集成时。使用
DelegatingFilterProxy
可以避免这些问题,因为它允许 Spring 控制 Filter 的实例化和配置。简化 Filter 定义: 使用
DelegatingFilterProxy
,你不需要在 web.xml 中定义 Filter。相反,你只需要定义一个 Filter Bean,并让DelegatingFilterProxy
负责将请求委托给这个 Bean。这简化了配置,并减少了 web.xml 的复杂性。支持 Filter 之间的依赖关系: 在某些情况下,你可能需要定义 Filter 之间的依赖关系(例如,确保某些 Filter 在其他 Filter 之前或之后执行)。使用
DelegatingFilterProxy
和 Spring 的FilterChainProxy
可以更容易地管理这些依赖关系。
总之,DelegatingFilterProxy
提供了一种将 Spring 管理的 Bean 转换为 Servlet Filter 的方式,这使得你可以利用 Spring 的依赖注入、生命周期管理和其他特性。这在构建大型、复杂的 Web 应用时尤其有用。
在Spring中,Filter和DelegatingFilterProxy有什么区别?
在Spring框架中,Filter
和 DelegatingFilterProxy
都用于处理Web请求,但它们的使用场景和功能有所不同。以下是它们之间的主要区别:
Servlet Filter vs. Spring Bean:
Filter
:是一个实现了javax.servlet.Filter
接口的Java类,通常在web.xml
中定义,由Servlet容器(如Tomcat)管理。DelegatingFilterProxy
:是一个Spring提供的代理类,它将Spring容器管理的Bean代理为一个Servlet Filter。这意味着DelegatingFilterProxy
本身是一个Filter,但它内部委托给一个Spring管理的Bean来执行实际的Filter逻辑。
依赖注入:
Filter
:由于是由Servlet容器管理,通常不能直接享受到Spring的依赖注入功能。DelegatingFilterProxy
:由于内部委托给Spring管理的Bean,因此可以享受到Spring的依赖注入功能,包括自动装配、生命周期回调等。
生命周期管理:
Filter
:其生命周期由Servlet容器管理,例如在Web应用启动和停止时创建和销毁Filter实例。DelegatingFilterProxy
:除了由Servlet容器管理外,还可以利用Spring的生命周期管理功能,例如使用@PostConstruct
和@PreDestroy
注解。
配置方式:
Filter
:通常在web.xml
中配置,或者使用Servlet 3.0+的注解方式。DelegatingFilterProxy
:作为Spring容器中的一个Bean来配置,可以在Spring配置文件或使用注解的方式定义。
使用场景:
Filter
:适用于那些不需要Spring特定功能,或者需要在Spring上下文之外运行的Filter。DelegatingFilterProxy
:适用于需要访问Spring上下文中的其他Bean,或者需要使用Spring特性(如依赖注入、生命周期管理)的Filter。
集成Spring Security:
- 如果你使用Spring Security,
DelegatingFilterProxy
是集成Spring Security过滤器链的推荐方式,因为它可以确保Spring Security的Filter能够作为Spring Bean被管理。
- 如果你使用Spring Security,
总结来说,DelegatingFilterProxy
提供了一种将Spring Bean转换为Servlet Filter的方式,使得你可以在Filter中使用Spring的特性,而普通的Filter
则不具备这些特性。在实际开发中,选择使用哪种方式取决于你的具体需求和场景。
总结
如果需要在 Filter
中使用Spring的特性,如依赖注入或生命周期管理,那么使用 DelegatingFilterProxy
是一个更好的选择。如果你的 Filter
不需要这些Spring特性,那么直接使用 Filter
可能是一个更简单直接的选择。
在Shiro与Spring集成时,通常建议使用 DelegatingFilterProxy
来代理Shiro的 Filter
,这样可以确保Shiro的 Filter
能够享受到Spring的依赖注入和生命周期管理功能。例如,在Spring配置文件中配置Shiro的 ShiroFilterFactoryBean
时,可以通过 DelegatingFilterProxy
来确保Shiro的 Filter
被Spring容器管理 。