Skip to content

记一次UnavailableSecurityManagerException异常

2415 字约 8 分钟

Apache ShiroFAQ

2024-10-20

问题背景

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 没有被正确地初始化或配置。这个异常可能由以下几个原因引起:

  1. ShiroFilterFactoryBean 配置问题:如果你在使用 Spring 集成 Shiro 时遇到了这个问题,可能是因为 ShiroFilterFactoryBean 没有被正确配置。确保你已经将 ShiroFilterFactoryBean 设置为一个 @Bean,并在其内部设置了 SecurityManager。例如:
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(securityManager);
    // 其他配置...
    return shiroFilterFactoryBean;
}
  1. 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;
}
  1. SecurityManager 未注入:确保 SecurityManager 已经被注入到你的应用上下文中,并且可以通过 SecurityUtils 获取到。如果没有注入,你需要配置一个 SecurityManager 的 bean,并确保它被 ShiroFilterFactoryBean 使用。

  2. Filter 执行顺序问题:如果你定义了自定义的过滤器,并且这些过滤器在 Shiro 的 ShiroFilterFactoryBean 之前执行,可能会导致 SecurityManager 未被正确设置。确保自定义过滤器是在 Shiro 过滤器之后执行的。

  3. Spring Boot 集成问题:如果你在使用 Spring Boot 集成 Shiro,可能需要额外注意配置的细节。例如,确保你的 ShiroFilterFactoryBean bean 名称与 DelegatingFilterProxy 中指定的名称一致。

  4. 确保 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 主要有以下几个原因:

  1. 依赖注入: 使用 DelegatingFilterProxy 可以让你的过滤器享受到 Spring 的依赖注入功能。这意味着你可以在过滤器中注入其他 Spring 管理的 Bean,例如服务(Service)、数据访问对象(Repository)等。这在直接使用 Filter 时是无法做到的,因为 Filter 不是由 Spring 管理的。

  2. 生命周期管理: Spring 管理的 Bean 有完整的生命周期管理,包括初始化和销毁回调。当你的过滤器作为 Spring Bean 时,你可以使用 @PostConstruct@PreDestroy 注解来执行初始化和清理工作。这有助于管理资源,如数据库连接或文件句柄。

  3. 集成 Spring Security: 如果你的应用使用 Spring Security,DelegatingFilterProxy 是集成 Spring Security 过滤器链的推荐方式。Spring Security 需要能够注入依赖项并管理生命周期,而 DelegatingFilterProxy 使得这一点成为可能。

  4. 一致的配置方式: 使用 DelegatingFilterProxy 可以保持你的配置方式一致性。在 Spring 应用中,几乎所有的组件都是通过 Spring 容器来管理的。通过使用 DelegatingFilterProxy,你的过滤器可以像其他组件一样被声明和配置。

  5. 避免 Filter 链问题: 直接在 web.xml 中定义 Filter 可能会导致 Filter 链的问题,特别是当与其他框架(如 Spring Security、Shiro 等)集成时。使用 DelegatingFilterProxy 可以避免这些问题,因为它允许 Spring 控制 Filter 的实例化和配置。

  6. 简化 Filter 定义: 使用 DelegatingFilterProxy,你不需要在 web.xml 中定义 Filter。相反,你只需要定义一个 Filter Bean,并让 DelegatingFilterProxy 负责将请求委托给这个 Bean。这简化了配置,并减少了 web.xml 的复杂性。

  7. 支持 Filter 之间的依赖关系: 在某些情况下,你可能需要定义 Filter 之间的依赖关系(例如,确保某些 Filter 在其他 Filter 之前或之后执行)。使用 DelegatingFilterProxy 和 Spring 的 FilterChainProxy 可以更容易地管理这些依赖关系。

总之,DelegatingFilterProxy 提供了一种将 Spring 管理的 Bean 转换为 Servlet Filter 的方式,这使得你可以利用 Spring 的依赖注入、生命周期管理和其他特性。这在构建大型、复杂的 Web 应用时尤其有用。

在Spring中,Filter和DelegatingFilterProxy有什么区别?

在Spring框架中,FilterDelegatingFilterProxy 都用于处理Web请求,但它们的使用场景和功能有所不同。以下是它们之间的主要区别:

  1. 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逻辑。
  2. 依赖注入:

    • Filter:由于是由Servlet容器管理,通常不能直接享受到Spring的依赖注入功能。
    • DelegatingFilterProxy:由于内部委托给Spring管理的Bean,因此可以享受到Spring的依赖注入功能,包括自动装配、生命周期回调等。
  3. 生命周期管理:

    • Filter:其生命周期由Servlet容器管理,例如在Web应用启动和停止时创建和销毁Filter实例。
    • DelegatingFilterProxy:除了由Servlet容器管理外,还可以利用Spring的生命周期管理功能,例如使用@PostConstruct@PreDestroy注解。
  4. 配置方式:

    • Filter:通常在 web.xml 中配置,或者使用Servlet 3.0+的注解方式。
    • DelegatingFilterProxy:作为Spring容器中的一个Bean来配置,可以在Spring配置文件或使用注解的方式定义。
  5. 使用场景:

    • Filter:适用于那些不需要Spring特定功能,或者需要在Spring上下文之外运行的Filter。
    • DelegatingFilterProxy:适用于需要访问Spring上下文中的其他Bean,或者需要使用Spring特性(如依赖注入、生命周期管理)的Filter。
  6. 集成Spring Security:

    • 如果你使用Spring Security,DelegatingFilterProxy 是集成Spring Security过滤器链的推荐方式,因为它可以确保Spring Security的Filter能够作为Spring Bean被管理。

总结来说,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容器管理 。

变更历史

最后更新于: 查看全部变更历史
  • docs: new update

    于 2024/12/24
  • update lastest vp

    于 2024/12/22
  • docs: add blog about springboot intergration about shiro

    于 2024/10/20