记一次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,可能需要额外注意配置的细节。例如,确保你的
ShiroFilterFactoryBeanbean 名称与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 容器管理 。
更新日志
8bd98-docs: 添加 blog/04_ARCHIVES 归档文档于
